今天一口气读完了 王福朋 的一篇博文《深入理解JavaScript原型和闭包系列》,发现了一条线索可以很好的诠释原型链。原话是这样的:
Instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。
Instanceof的判断规则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false
当我还是小白的时候感觉对javascript原型理解很困难的原因就是我根本不知道他们在浏览器中是如何创建的,彼此的关系是怎样的,啥是对象?{}就是吗?...... 后来才知道这只不过是js的语法,而在浏览器内存中则是另一种形态。
下面这张图说明了这几个概念以及他们之间的关系。
Object.prototype 属性保存的是对 Object的原型对象的引用。
这里要特别解释一下引用的概念是源于我是小白的时候对于程序中各种概念理解的困惑,当时我以为一个变量就代表一个值,比如:
当我在学习python的时候渐渐明白,变量保存的是对值的引用,因为在python中,内存中的数值只有“0,1,2...8,9”这几个数,我们创建一个数值变量到时候,实际每一位数字是对它们其中一个值的引用。
同样,在浏览器中,JavaScript声明变量的时候会在内存中创建相应的对象,然后将这个对象的引用赋给这个变量。只不过JavaScript的这四种基本类型 number、string、boolean、undefined 以及 null(它实际是一个对象,只不过没办法把它的值再改变了,所以看上去跟基本类型的使用方式差不多)没有下属的属性和方法,也就不存在被修改的可能,所以他们最多被引用一次。
当a被重新赋值的时候,失去了对数值 1 的引用,重新建立对数值 2 的引用。
但对于引用类型 function和object来说,他们存在子属性,也就存在了修改的可能
当两个变量指向同一个对象的时候(函数也是一种对象),我们通过foo修改这个对象的x属性为"foo",当通过fuu变量访问的时候也会看到foo的x属性被修改成了"foo"。
变量foo和fuu保存的是对函数对象的引用,这点明白了我们再看Object.prototype。
Object.prototype保存的是对Object原型对象的引用。prototype存在于函数对象中,一般实例对象是没有这个属性的,但可以通过__proto__属性去访问创建这个对象的 函数对象的 原型对象。__proto__不是标准属性,老式浏览器中不存在,但是对于理解原型链至关重要。
这段代码很形象的描述了实例对象与原型对象的关系。原型对象就好比是卵,构造函数是一个老母鸡,实例对象就好比是蛋,卵被下出来就是蛋了。蛋有一个__proto__标记,他可以通过__proto__去找自己出生前的住所(卵巢)。
原型继承
__proto__像一条线一样,将原型对象串联起来。
这是一段原型继承的示例代码(节选自JavaScript高级程序设计)。我们通过__proto__属性一层一层向上找就能描绘出整个原型链。
当实例对象调用属性和方法的时候会依从从自己、构造函数的原型对象、父级的原型对象...一直找到Object的原型对象,直至null。这条链上的原型对象的属性和方法都可以被实例对象调用。
这张图描绘了最基本的原型链查找方式。
Instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。
Instanceof的判断规则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false
instanceof运算符被用来判断对象是否是另一个对象的实例,通常用来判断这种情况:
var Foo = function(){this.name ='Foo';};
var foo = new Foo();
foo instanceof Foo; // true
foo.__proto__ === Foo.prototype; // true
foo instanceof Object; //true
foo.__proto__.__proto__ === Foo.prototype.__proto__; // true
首先先明确几个概念:函数对象、实例对象、原型对象、构造函数、引用。
当我还是小白的时候感觉对javascript原型理解很困难的原因就是我根本不知道他们在浏览器中是如何创建的,彼此的关系是怎样的,啥是对象?{}就是吗?...... 后来才知道这只不过是js的语法,而在浏览器内存中则是另一种形态。
下面这张图说明了这几个概念以及他们之间的关系。
先来看看图中央的Object函数对象:
typeof Object
//"function"
它是JavaScript内置的一个对象,通常用来创建实例对象。o1,o2均是由 new Object() 语法创建出来的实例对象,当函数通过new关键字来调用的时候,那么这个函数被称为构造函数。可以说o1,o2的构造函数是Object,而每一个实例对象都会被一个构造函数所创建。
Object.prototype 属性保存的是对 Object的原型对象的引用。
这里要特别解释一下引用的概念是源于我是小白的时候对于程序中各种概念理解的困惑,当时我以为一个变量就代表一个值,比如:
var a = 11;
var foo = function(){console.log('foo')};
var obj = {x: 11, y: true};
我会认为 变量a值是11,变量foo是一个函数,变量obj是一个对象,其中有俩属性x和y。
当我在学习python的时候渐渐明白,变量保存的是对值的引用,因为在python中,内存中的数值只有“0,1,2...8,9”这几个数,我们创建一个数值变量到时候,实际每一位数字是对它们其中一个值的引用。
同样,在浏览器中,JavaScript声明变量的时候会在内存中创建相应的对象,然后将这个对象的引用赋给这个变量。只不过JavaScript的这四种基本类型 number、string、boolean、undefined 以及 null(它实际是一个对象,只不过没办法把它的值再改变了,所以看上去跟基本类型的使用方式差不多)没有下属的属性和方法,也就不存在被修改的可能,所以他们最多被引用一次。
var a=b=1;
console.log(a); // 1
console.log(b); // 1
a=2;
console.log(a); // 2
console.log(b); // 1
当a被重新赋值的时候,失去了对数值 1 的引用,重新建立对数值 2 的引用。
但对于引用类型 function和object来说,他们存在子属性,也就存在了修改的可能
var foo=fuu = function(){};
foo.x = 'foo';
console.log(foo.x); // "foo"
console.log(fuu.x); // "foo"
当两个变量指向同一个对象的时候(函数也是一种对象),我们通过foo修改这个对象的x属性为"foo",当通过fuu变量访问的时候也会看到foo的x属性被修改成了"foo"。
变量foo和fuu保存的是对函数对象的引用,这点明白了我们再看Object.prototype。
Object.prototype保存的是对Object原型对象的引用。prototype存在于函数对象中,一般实例对象是没有这个属性的,但可以通过__proto__属性去访问创建这个对象的 函数对象的 原型对象。__proto__不是标准属性,老式浏览器中不存在,但是对于理解原型链至关重要。
o1.__proto__ === Object.prototype // true
o2.__proto__ === Object.prototype // true
Object.prototype.constructor === Object // true
o1.constructor === Object
o2.constructor === Object
这段代码很形象的描述了实例对象与原型对象的关系。原型对象就好比是卵,构造函数是一个老母鸡,实例对象就好比是蛋,卵被下出来就是蛋了。蛋有一个__proto__标记,他可以通过__proto__去找自己出生前的住所(卵巢)。
原型继承
__proto__像一条线一样,将原型对象串联起来。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.getSubValue = function(){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); // true
这是一段原型继承的示例代码(节选自JavaScript高级程序设计)。我们通过__proto__属性一层一层向上找就能描绘出整个原型链。
instance.__proto__ === SubType.prototype // true
instance.__proto__.__proto__ === SuperType.prototype // true
instance.__proto__.__proto__.__proto__ === Object.prototype // true
instacne.__proto__ === instance.constructor.prototype // true
当实例对象调用属性和方法的时候会依从从自己、构造函数的原型对象、父级的原型对象...一直找到Object的原型对象,直至null。这条链上的原型对象的属性和方法都可以被实例对象调用。
这张图描绘了最基本的原型链查找方式。