JavaScript - this详解 (二)
用栗子说this
Bug
年年有,今年特别多
对于JavaScript
这么灵活的语言来说,少了this
怎么活!
function
函数 this
对于没有实例化的function,我们称之为函数,即没有用new关键字调用的函数,对它们来说,this一律指代全局。
上栗子
var position = "outer";
function test(){
position = "inner";
}console.log(this.position); // outer test();console.log(this.position); // inner原因:内部未用var声明发生“变量提升”,污染全局变量。
构造函数 this
对于被实例化的function,我们称之为构造函数,及使用new关键字调用的function,对于它们来说,this会被改变,指向实例。
上栗子
var name = 'person'; // 全局this赋上属性namefunction Person(name){ this.name = name; this.say = function () { console.log('My name is '+this.name);
} this.show = function () { console.log(this);
}
}var william = new Person('william'); //通过new关键字创建实例,改变函数内部this指向console.log(this.name); //personwilliam.say(); //My name is williamwilliam.show(); //Person {name: "william", say: ...注解:通过这个栗子,我们可以看出,通过创建构造函数的实例,使得this的指向改变,指向了实例本身。
原型对象 this
每一个函数对象在创建的时候,都会自动添加一个prototype属性,这个prototype其实是一个指针,指向这个函数的原型对象。
你可以通过prototype指针给这个函数对象的原型对象添加属性,在实例化函数对象后,可以通过this来访问原型对象上的属性。
上栗子
function Person(){ console.log(this.name); } Person.prototype.name = "william"; //给原型对象上赋name属性
var person = new Person(); // 通过this.name访问原型对象上的属性,打印 "william"
这还不够,咱们为函数对象直接添加同名,而不在原型对象上添加
function Person(){
this.name = "Jack";
console.log(this.name);
}
Person.prototype.name = "william";
var person = new Person(); // 打印 "Jack"
这里有一个值得注意的地方:
当你
构造函数
中存在和原型对象
中同名
的属性
或方法
时,原型对象中的属性或方法会被隐藏
,你只会访问到构造函数中的属性或方法`
Object.create
this
通过Object.create创建函数对象实例,而不使用new关键字,也就是说它不会去调用构造函数
上栗子
function Person(name){
this.name = name;
this.showName = function () {
console.log(this.name + ' in constructor');
}
} //在构造函数中的属性和方法,均同名
Person.prototype.name = "jack";
Person.prototype.showName = function () {
console.log(this.name + ' in prototype');
}; //在原型对象中的属性和方法,均同名
var william = new Person("william");v
ar jack = Object.create(Person.prototype);
william.showName(); // william in constructorjack.showName(); // jack in prototype注解:
- 使用new关键字创造的实例调用了构造函数的属性和方法
- 使用Object.create创造的实例调用了原型对象的属性和方法
原型链 this
原型链用于实现继承,当没有找到需要属性或方法时,会顺着原型链向上继续寻找。
上栗子
function Father(){
this.name = 'father';
} //可以看做"父类"Father.prototype.showName = function () {
console.log(this.name);
};function Son(){
this.name = 'son';
} //可以看做"子类"
Son.prototype = new Father(); //这步是关键!
var mike = new Son();
mike.showName(); // son注解:
- 首先要强调的是JavaScript中没有"父类","子类","继承"这样的东西,我们只是通过原型链来实现这样的设计模式。
- 通过这一步
Son.prototype = new Father();
我们将"子类"的prototype指针指向了"父类"函数对象
设计模式:继承
为什么这样就能实现继承呢?
首先要想明白为什么实例能访问构造函数的原型对象
在JavaScript中,有一个内部的属性,在火狐,谷歌浏览器中,将这个内部属性暴露了出来,就是它 __proto__
上栗子(浏览器中)
mike.__proto__ // Father {name: "father", showName: function}
Son.prototype //Father {name: "father", showName: function}
mike.__proto__ === Son.prototype // true通过这个内部属性,我们可以访问到实例的构造函数的原型对象
再来一枚(浏览器中)
mike.__proto__.__proto__ //Father {showName: function}
Father.prototype //Father {showName: function}
mike.__proto__.__proto__ === Father.prototype // true
我们现在通过__proto__访问到了"父类"的原型对象!
继承得以实现!
补充一句:
我们可以访问到Son的name属性的值,却访问不到Father的name属性的值,是因为
- mike是Son的实例(实例能做什么...?)
- Father中的name没有对外开放,可以看做是私有属性
总结
主要探讨了
- function
作为函数、构造函数、原型对象时,this
的指代情况
- 扩展了另一种实例化方式Object.create
- 细说了原型链原理和实现,模拟了继承的设计模式