JavaScript原型、原型链详解

先来个图镇压一下
file
Func(){}为一个函数,newFunc1,newFunc2是使用Func函数实例化出来的对象,即Func为newFunc1、newFunc2的构造函数,Function为函数的构造函数,Object为对象原型。
名词解释:
1、prototype,是函数独有的属性,是一个指针,从一个函数指向一个对象,这个对象被我们成为原型对象,包含了所有实例共享的属性和方法。含义是函数的原型对象。
2、proto,是对象独有的属性(说和不说没啥区别,JS中万物皆对象),是在原型链查询时候实际使用的属性,总是指向prototype属性,也就是说_proto_总是指向构造函数的原型对象,即构造函数的prototype。
3、constructor,每个函数都有一个prototype属性,在prototype属性中有一个constructor属性,这个属性总是指向创建对象的函数本身。这也就说明constroctor也是函数独有的。上述例子中Func的constructor属性则指向Func(){}本身。
由图可以看出,原型链就是依靠着隐式原型proto和prototype,实例化对象在调用某个方法时候,会先从proto中(它的构造函数的prototype)寻找,如果没有找到,则从构造函数的proto中(Object的prototype)寻找,如果还没找到则返回null,如果找到了则返回方法进行调用。
我们来看一个例子:

Function.prototype.sex(){
    console.log('function');
}
Object.prototype.sex(){
    console.log('object');
}
function Foo(){};
let newFun1 = new Foo();
newFun1.sex(); //object

先给Function、Object的原型对象上添加一个sex方法,输出不一样。然后使用构造函数实例化出一个newFun1,再调用sex方法。此时输出object,并未输出function。别急,我们再改一下

Function.prototype.sex(){
    console.log('function');
}
Object.prototype.sex(){
    console.log('object');
}
function Foo(){};
let newFun1 = Foo;
newFun1.sex(); //function

此时,输出了function。
咦~为啥嘞,对比两者,前者是用new操作符进行实例化,后者是直接赋值,再来看看new操作符干了什么。new操作符首先是创建了一个空对象,然后将构造函数的prototype赋值给空对象的proto(隐式原型),然后将构造函数的this绑定给新对象的执行环境,最后查看绑定的执行环境中是否有返回值,如果有返回值则将一个新对象返回,如果没有返回值或者返回值非对象则利用空obj进行返回。
原因出现了,new操作符将构造函数的prototype赋值给了空对象的proto,所以,实例对象在调用方法时候的原型链如下:
1、newFun1->Foo.prototype->Object.prototype
在不使用new操作符时候,调用方法的原型链如下:
2、newFun1->Foo.prototype->Function.prototype->Object.prototype。此时,在Function的prototype上有sex方法,则返回方法,停止寻找。
在使用String、Number等构造函数时候不会出现此问题,譬如:

String.prototype.sex(){
    console.log('string');
}
Object.prototype.sex(){
    console.log('object');
}
let newString = new String('1221232');
let newString2 = '1231231asdf';
newString.sex();//string
newString2.sex();//string

在不使用new操作符,创建了一个基础类型数据,调用方法,也会按照newString->String.prototype->Object.prototype进行原型链查找。因为我们在基础类上调用方法时候js都会做一个装箱操作,基础类本身是没有方法的,当我们调用方法时候,js会根据数据类型调用相关的构造器实例化一个对应的对象,然后在此对象上调用相关方法,进行原型链的查找。
在《javascript高级程序设计》中有这样一句话:
每当读取一个基本类型的时候,后台就会创建一个对应的基本包装类型对象,从而让我们能够调用一些方法来操作这些数据。
譬如:

let newString = 'fasdfa';
newString.subString(2);

1、使用String构造器创建一个string实例
2、在实例上进行原型链查找方法,调用方法
3、调用结束后,销毁实例
所以对于非function的基础类型,不管是否使用new操作符,都会按照上述原型链进行查找。

file

讨论数量: 2

牛批!!!!!!!!!file

4年前

骚~~~

4年前

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!