return { name:obj }; return 后紧接回车被自动加入了; 成了 return ; { name:obj }; 正确写法 return { name:obj }; 对象:属性集合。 函数:可以看作有()运算符,有prototype的对象。两种调用方式:普通调用,new 对象。 对象: 由构造函数创建。创建时指定_proto_为构造函数的prototype的对象. 可以看作对象的公共属 性。 js代码执行前准备工作
这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。 函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。 另外一点不同在于,函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域。至于“自由变量”和“作用域”是后面要专门拿出来讲述的重点,这里就先点到为止。用一个例子说明一下: 这两个a 不是同一个,作用域不同。 好了,总结完了函数的附加内容,我们就此要全面总结一下上下文环境的数据内容。 全局代码的上下文环境数据内容为:
如果代码段是函数体,那么在此基础上需要附加:
给执行上下文环境下一个通俗的定义——在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空。
函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域。
如: f(); 则f中的this 一般为window
情况1:构造函数 所谓构造函数就是用来new对象的函数。其实严格来说,所有的函数都可以new一个对象,但是有些函数的定义是为了new一个对象,而有些函数则不是。另外注意,构造函数的函数名第一个字母大写(规则约定)。例如:Object、Array、Function等。 以上代码中,如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象。
注意,以上仅限new Foo()的情况,即Foo函数作为构造函数的情况。如果直接调用Foo函数,而不是new Foo(),情况就大不一样了。 这种情况下this是window,我们后文中会说到。
情况2:函数作为对象的一个属性 如果函数作为对象的一个属性时,并且作为对象的一个属性被调用时,函数中的this指向该对象。 以上代码中,fn不仅作为一个对象的一个属性,而且的确是作为对象的一个属性被调用。结果this就是obj对象。 fn的调用者为obj 所以this 为obj。
注意,如果fn函数不作为obj的一个属性被调用,会是什么结果呢? 如上代码,如果fn函数被赋值到了另一个变量中,并没有作为obj的一个属性被调用,那么this的值就是window,this.x为undefined。
情况3:函数用call或者apply调用 当一个函数被call和apply调用时,this的值就取传入的对象的值。至于call和apply如何使用,不会的朋友可以去查查其他资料,本系列教程不做讲解。
情况4:全局 & 调用普通函数 在全局环境下,this永远是window,这个应该没有非议。
普通函数在调用时,其中的this也都是window。 以上代码很好理解。 不过下面的情况你需要注意一下: 函数f虽然是在obj.fn内部定义的,但是它仍然是一个普通的函数,this仍然指向window。
完了。 看到了吧,this有关的知识点还是挺多的,不仅多而且非常重要。 最后,既然提到了this,有必要把一个非常经典的案例介绍给大家,又是jQuery源码的。 以上代码是从jQuery中摘除来的部分代码。jQuery.extend和jQuery.fn.extend都指向了同一个函数,但是当执行时,函数中的this是不一样的。 执行jQuery.extend(…)时,this指向jQuery;执行jQuery.fn.extend(…)时,this指向jQuery.fn。 这样就巧妙的将一段代码同时共享给两个功能使用,更加符合设计原则。
“javascript没有块级作用域”。所谓“块”,就是大括号“{}”中间的语句。 javascript除了全局作用域之外,只有函数可以创建的作用域。 所以,我们在声明变量时,全局代码要在代码前端声明,函数中要在函数体一开始就声明好。除了这两个地方,其他地方都不要出现变量声明。而且建议用“单var”形式。 作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。例如以上代码中,三个作用域下都声明了“a”这个变量,但是他们不会有冲突。各自的作用域下,用各自的“a”。 说到这里,咱们又可以拿出jquery源码来讲讲了。 jQuery源码的最外层是一个自动执行的匿名函数: 为什么要这样做呢? 原因就是在jQuery源码中,声明了大量的变量,这些变量将通过一个函数被限制在一个独立的作用域中,而不会与全局作用域或者其他函数作用域的同名变量产生冲突.“自由变量”: 在A作用域外声明,在A作用域中使用的变量x,对于A作用域来说,x就是一个自由变量。 作用域链: 我们拿文字总结一下取自由变量时的这个“作用域链”过程:(假设a是自由量) 第一步,现在当前作用域查找a,如果有则获取并结束。如果没有则继续; 第二步,如果当前作用域是全局作用域,则证明a未定义,结束;否则继续; 第三步,(不是全局作用域,那就是函数作用域)将创建该函数的作用域作为当前作用域; 第四步,跳转到第一步。
以上代码中:第13行,fn()返回的是bar函数,赋值给x。执行x(),即执行bar函数代码。取b的值时,直接在fn作用域取出。取a的值时,试图在fn作用域取,但是取不到,只能转向创建fn的那个作用域中去查找,结果找到了。 闭包持有在其定义的那一刻的外部自由变量。 对比:objective-c中的block 持有外部一层定义的变量的只读拷贝。
至于“闭包”这个词的概念的文字描述,确实不好解释,我看过很多遍,但是现在还是记不住。 但是你只需要知道应用的两种情况即可——函数作为返回值,函数作为参数传递。 第一,函数作为返回值 如上代码,bar函数作为返回值,赋值给f1变量。执行f1(15)时,用到了fn作用域下的max变量的值。至于如何跨作用域取值,可以参考上一节。
第二,函数作为参数被传递 如上代码中,fn函数作为一个参数被传递进入另一个函数,赋值给f参数。执行f(15)时,max变量的取值是10,而不是100。 上一节讲到自由变量跨作用域取值时,曾经强调过:要去创建这个函数的作用域取值,而不是“父作用域”。理解了这一点,以上两端代码中,自由变量如何取值应该比较简单。 fn()调用完成。按理说应该销毁掉fn()的执行上下文环境,但是这里不能这么做。注意,重点来了:因为执行fn()时,返回的是一个函数。函数的特别之处在于可以创建一个独立的作用域。而正巧合的是,返回的这个函数体中,还有一个自由变量max要引用fn作用域下的fn()上下文环境中的max。因此,这个max不能被销毁,销毁了之后bar函数中的max就找不到值了。 因此,这里的fn()上下文环境不能被销毁,还依然存在与执行上下文栈中。 |
|
来自: quasiceo > 《javascript》