1、第 1 节 JavaScript this 关键字精解1 JavaScript call()函数:call()方法/apply()的意义在于扩充函数的作用域!JavaScript func.call()定义: 若 IsCallable(func)为 false,则抛出 TypeError异常。 将 argList置空。 若方法调用不止一个参数,则按从左向右的顺序将参数添加到 argList中。 返回 func中的方法调用结果,提供 thisArg作为 this的值,并将 argList作为参数列表。1 Make an argument list (argList) out of paramet
2、ers 1 through the end2 The first parameter is thisValue3 Invoke the function with this set to thisValue and the argList as its argument list例子:function hello(thing) console.log(this + “ says hello “ + thing);hello.call(“Yehuda“, “world“) /= Yehuda says hello world以上调用简化:function hello(thing) console
3、.log(“Hello “ + thing);/ this:hello(“world“)/ desugars to:hello.call(window, “world“);4 但 ECMAScript5 规范对此作了进一步限定,只在严格模式下才会将 thisValue 绑定到 window,而一般情况下,绑定到 undefined。原文:a function invocation like fn(.args) is the same as fn.call(window ES5-strict: undefined, .args)./ this:hello(“world“)/ desugars t
4、o:hello.call(undefined, “world“);5 内联函数(inline function):(function() )() is the same as(function() ).call(window ES5-strict: undefined)2 成员函数2.1 内置成员函数var person = name: “Brendan Eich“,hello: function(thing) console.log(this + “ says hello “ + thing);/ this:person.hello(“world“)/ desugars to this:pe
5、rson.hello.call(person, “world“);2.2 外成员函数function hello(thing) console.log(this + “ says hello “ + thing);person = name: “Brendan Eich“ person.hello = hello;person.hello(“world“) / still desugars to person.hello.call(person, “world“)hello(“world“) / “object DOMWindowworld“3 使用 Function.prototype绑定将
6、 this绑定到指定对象的方法:var person = name: “Brendan Eich“,hello: function(thing) console.log(this.name + “ says hello “ + thing);var boundHello = function(thing) return person.hello.call(person, thing); boundHello(“world“);上述方法一般化:var bind = function(func, thisValue) return function() return func.apply(this
7、Value, arguments);var boundHello = bind(person.hello, person);boundHello(“world“) / “Brendan Eich says hello world“ECMAScript提供了 bind()方法实现上述功能。var boundHello = person.hello.bind(person);boundHello(“world“) / “Brendan Eich says hello world“常用于原生函数作为被调函数:var person = name: “Alex Russell“,hello: funct
8、ion() console.log(this.name + “ says hello world“); $(“#some-div“).click(person.hello.bind(person);/ when the div is clicked, “Alex Russell says hello world“ is printed第 2 节 分类讲解 JavaScript this1 作为对象方法调用在 JavaScript 中,函数也是对象,因此函数可以作为一个对象的属性,此时该函数被称为该对象的方法,在使用这种调用方式时,this 被自然绑定到该对象。var point = x : 0
9、, y : 0, moveTo : function(x, y) this.x = this.x + x; this.y = this.y + y; ; point.moveTo(1, 1)/this 绑定到当前对象,即 point 对象2 作为函数调用函数也可以直接被调用,此时 this 绑定到全局对象。在浏览器中,window 就是该全局对象。比如下面的例子:函数被调用时,this 被绑定到全局对象,接下来执行赋值语句,相当于隐式的声明了一个全局变量,这显然不是调用者希望的。代码 1:function makeNoSense(x) this.x = x; makeNoSense(5); x
10、;/ x 已经成为一个值为 5 的全局变量代码 2:var point = x : 0, y : 0, moveTo : function(x, y) / 内部函数var moveX = function(x) this.x = x;/this 绑定到了哪里?; / 内部函数var moveY = function(y) this.y = y;/this 绑定到了哪里?; moveX(x); moveY(y); ; point.moveTo(1, 1); point.x; /=0 point.y; /=0 x; /=1 y; /=1代码 3:var point = x : 0, y : 0,
11、moveTo : function(x, y) var that = this; / 内部函数var moveX = function(x) that.x = x; ; / 内部函数var moveY = function(y) that.y = y; moveX(x); moveY(y); ; point.moveTo(1, 1); point.x; /=1 point.y; /=13 作为构造函数调用JavaScript 支持面向对象式编程,与主流的面向对象式编程语言不同,JavaScript 并没有类(class)的概念,而是使用基于原型(prototype)的继承方式。相应的,Java
12、Script 中的构造函数也很特殊,如果不使用 new 调用,则和普通函数一样。作为又一项约定俗成的准则,构造函数以大写字母开头,提醒调用者使用正确的方式调用。如果调用正确,this 绑定到新创建的对象上。代码:function Point(x, y) this.x = x; this.y = y; 4 使用 apply 或 call 调用让我们再一次重申,在 JavaScript 中函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。这两个方法异常强大,他们允许切换函数执行的上下文环境(context) ,即 this 绑定的对象。很多 JavaScript 中的技巧以
13、及类库都用到了该方法。让我们看一个具体的例子:function Point(x, y) this.x = x; this.y = y; this.moveTo = function(x, y) this.x = x; this.y = y; var p1 = new Point(0, 0); var p2 = x: 0, y: 0; p1.moveTo(1, 1); / 将 moveTo()方法应用到对象 p2上,并将对象上下文(context)切换为 p2/ 也可以使用 call(),和 apply()类似,但参数为多个独立传入/ p1.moveTo.call(p2, 10, 10);p1.
14、moveTo.apply(p2, 10, 10);5 JavaScript函数执行首先,创建 ExecutionContext对象,作为函数执行的上下文。创建上下文时,创建arguments变量,包含函数调用的实参值。其次,创建作用域链。首先,创建初始化函数的形参表,即为 arguments变量中对应的值,若没有初始化,则为 undefined。其次,若函数包含内部函数,则初始化对应的值;若没有内部函数,则初始化局部变量为 undefined(具体在 ExecutionContext创建成功后,函数开始运行时,才会实际赋值) 。最后,为 this变量赋值(见以上几种类型) 。至此函数的执行环境
15、(ExecutionContext)创建成功,函数开始逐行执行,所需变量均从之前构建好的执行环境(ExecutionContext)中读取。6 Function.bind有了前面对于函数执行环境的描述,我们来看看 this 在 JavaScript 中经常被误用的一种情况:回调函数。JavaScript 支持函数式编程,函数属于一级对象,可以作为参数被传递。请看下面的例子 myObject.handler 作为回调函数,会在 onclick 事件被触发时调用,但此时,该函数已经在另外一个执行环境(ExecutionContext)中执行了,this 自然也不会绑定到 myObject 对象上。
16、代码:button.onclick = myObject.handler; / this绑定到 button,而非 myObject可以使用 bind方法实现函数到对象的绑定:代码:var bind = function(func, thisValue) return function() return func.apply(thisValue, arguments);var boundHello = bind(person.hello, person);boundHello(“world“) / “Brendan Eich says hello world“ECMAScript提供了 bin
17、d()方法实现上述功能。7 eval()方法JavaScript 中的 eval 方法可以将字符串转换为 JavaScript 代码,使用 eval 方法时,this 指向哪里呢?答案很简单,看谁在调用 eval 方法,调用者的执行环境(ExecutionContext)中的 this 就被 eval 方法继承下来了。第 3 节 Object.create()一、create()1 语法Object.create(proto , propertiesObject )2 参数proto: 一个对象,作为新创建对象的原型.propertiesObject:一个对象值,可以包含若干个属性,属性名为新
18、建对象的属性名,属性值为那个属性的属性描述符对象.3 描述如果 proto参数的值不是 null或者对象值,则会 TypeError异常.4 应用4.1 创建一个原型为 null的空对象var o;/ 创建一个原型为 null的空对象o = Object.create(null);4.2 字面量创建对象与 create()/ (1)创建不带属性的对象o = ;/ 以字面量方式创建的空对象就相当于:o = Object.create(Object.prototype);4.3 创建带有属性的对象o = Object.create(Object.prototype, / foo会成为所创建对象的数
19、据属性foo: writable:true, configurable:true, value: “hello“ ,/ bar会成为所创建对象的访问器属性bar: configurable: false,get: function() return 10 ,set: function(value) console.log(“Setting o.bar to“, value) )4.4 用函数创建对象function Constructor()o = new Constructor();/ 上面的一句就相当于:/ 当然 ,如果在 Constructor函数中有一些初始化代码 ,Object.cr
20、eate不能执行那些代码o = Object.create(Constructor.prototype);4.5 创建一个以另一个空对象为原型,且拥有一个属性 p的对象/ 创建一个以另一个空对象为原型,且拥有一个属性 p的对象o = Object.create(, p: value: 42 )/ 省略了的属性特性默认为 false,所以属性 p是不可写,不可枚举, 不可配置的:o.p = 24o.p/42o.q = 12for (var prop in o) console.log(prop)/“q“delete o.p/false4.6 创建一个可写的,可枚举的 ,可配置的属性 p/创建一个
21、可写的,可枚举的,可配置的属性 po2 = Object.create(, p: value: 42, writable: true, enumerable: true, configurable: true );5 create实现单继承/Shape - 父类function Shape() this.x = 0;this.y = 0;/ 定义父类的方法该方法属于所有对象Shape.prototype.move = function(x, y) this.x += x;this.y += y;console.info(“Shape moved.“);/ 第1步:定义子类构造方法:Rectan
22、gle - 子类function Rectangle() / this指向父类 ShapeShape.call(this); /调用父类构造方法/ 第2步:使子类的 prototype属性/对象指向父类的 prototype对象/ 属性Rectangle.prototype = Object.create(Shape.prototype);var rect = new Rectangle();rect instanceof Rectangle /true.rect instanceof Shape /true.rect.move(); /Outputs, “Shape moved.“6 实现多
23、继承/ 使用多个 call实现多继承function MyClass() SuperClass.call(this);OtherSuperClass.call(this);MyClass.prototype = Object.create(SuperClass.prototype); /inheritmixin(MyClass.prototype, OtherSuperClass.prototype); /mixinMyClass.prototype.myMethod = function() / do a thing;Mixin()函数的实现代码:if (!Object.create) Ob
24、ject.create = function (o) if (arguments.length 1) throw new Error(Object.create implementation only accepts the first parameter.);function F() F.prototype = o;return new F();注:只实现了创建一个指定原型的对象的功能,而不能同时添加特定的属性,也就是没有实现原生的 Object.create函数中的第二个参数.7 浏览器支持Feature Chrome Firefox (Gecko) Internet Explorer O
25、pera SafariBasic support 5 4.0 (2) 9 11.60 5第 4 节 Object.extend()1 Object.extend()实现代码 1:/ 一个静态方法表示继承, 目标对象将拥有源对象的所有属性和方法Object.extend = function(destination, source) for (var property in source) / 利用动态语言的特性, 通过赋值动态添加属性与方法destinationproperty = sourceproperty; return destination; / 返回扩展后的对象/ Object 的
26、 extend 实例方法Object.prototype.extend = function(object) return Object.extend.apply(this, this, object); 代码 2:Object.extend(Object, / 一个静态方法, 传入一个对象, 返回对象的字符串表示inspect: function(object) try / 处理 undefined 情况if (object = undefined) return undefined; if (object = null) return null; / 处理 null 情况/ 如果对象定义了
27、 inspect 方法, 则调用该方法返回, 否则返回对象的toString()值return object.inspect ? object.inspect() : object.toString(); catch (e) / 处理异常情况if (e instanceof RangeError) return .; throw e;,keys: function(object) / 一个静态方法, 传入一个对象, 返回该对象中所有的属性, 构成数组返回var keys = ;for (var property in object)keys.push(property); / 将每个属性压入到
28、一个数组中 return keys;,/ 一个静态方法, 传入一个对象, 返回该对象中所有属性所对应的值, / 构成数组返回values: function(object) var values = ;/ 将每个属性的值压入到一个数组中for (var property in object) values.push(objectproperty);return values;,/ 一个静态方法, 传入一个对象, 克隆一个新对象并返回clone: function(object) return Object.extend(, object););2 JavaScript类2.1 静态成员/ 给
29、function 增加静态成员 memberfunction.member=function() 2.2 实例成员代码 1:function.prototype.member=function() 代码 2:function.prototype= member1:function(), member2:“abc“, member3:function() 实例方法中就可以引用 this 指针,指向由这个类实例化的对象本身。2.3 prototype定义prototype 是任何一个类(函数)内部保留的一个静态成员。它的功能就是存储这个类的所有成员指针,但这些成员都只是原型,没有经过初始化2.4
30、枚举变量的所有成员for(var p in object)功能:列举一个变量的所有成员。若变量为函数,则列出所有静态成员。若变量为对象,则列出所有实例成员。p 为字符串类型,表示成员名称。引用成员格式:variabel.member ,variabel“member“。2.5 apply()方法格式:method.apply(object,arguments); 意义:将 method这个方法应用到 object去执行,参数是 arguments这个数组。注意:method并不是 object的成员。等价于 object.method(arguments)。调用者:apply()方法的调用者是
31、一个方法。3 使用 extend()方法实现继承3.1 extend()作为对象的实例成员Object.prototype.extend = function(object) return Object.extend.apply(this, this, object); 说明:假定 obj为调用实例成员 extend()的对象,则 Object.extend.apply(this, this, object);意义是,将静态方法 Object.extend()方法应用到 obj,并将 extend()的参数(this, object)传递给它。注意,这两个 this 意义不同。第 1 个参数
32、this 的意义是 obj,而第 2 个参数的 this(中的 this),是 Object.extend()方法参数的一部分。3.2 extend继承语法b.prototype.extend(a.prototype); / 让 b 继承 a。 注意:prototype 是存放方法原型指针,extend 方法没有初始化,不能使用!要使用extend,就必须实例化一个对象。b.prototype=(new a().extend(b.prototype); / b 继承 a,即将 b.prototype 复制到对象 a 中,再把这个新对象赋给 b.prototype,完成 b 继承 a。继承的一般
33、语法:b.prototype=(new a().extend(); 3.3 示例代码/ 给 Object 对象增加静态方法 extend,该方法的作为复制 source/ 有所有属性和方法到 destination Object.extend = function(destination, source) for (property in source) destinationproperty = sourceproperty; return destination; var dog = function(name) this.name = name; /将 printName 方法复制给 dog.prototype Object.extend(dog.prototype, printName:function() alert(this.name); ); var a = new dog(“dog“); a.printName();