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赋上属性name

function 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);          //person

william.say();                   //My name is william

william.show();                  //Person {name: "william", say: ...


注解:经过这个栗子,咱们能够看出,经过建立构造函数的实例,使得this的指向改变,指向了实例自己。

原型对象 this

每个函数对象在建立的时候,都会自动添加一个prototype属性,这个prototype实际上是一个指针,指向这个函数的原型对象。

你能够经过prototype指针给这个函数对象的原型对象添加属性,在实例化函数对象后,能够经过this来访问原型对象上的属性。

上栗子this

function Person(){
    console.log(this.name);
}

Person.prototype.name = "william";    //给原型对象上赋name属性

var person = new Person();            // 经过this.name访问原型对象上的属性,打印 "william"

这还不够,我们为函数对象直接添加同名,而不在原型对象上添加prototype

function Person(){
    this.name = "Jack";
    console.log(this.name);
}

Person.prototype.name = "william";

var person = new Person();           // 打印 "Jack"

这里有一个值得注意的地方:设计

当你构造函数中存在和原型对象同名属性方法时,原型对象中的属性或方法会被隐藏,你只会访问到构造函数中的属性或方法`指针

Object.create this

经过Object.create建立函数对象实例,而不使用new关键字,也就是说它不会去调用构造函数

上栗子code

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");
var jack = Object.create(Person.prototype);

william.showName();  // william in constructor

jack.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 - 细说了原型链原理和实现,模拟了继承的设计模式

相关文章
相关标签/搜索