前言
一般谈到js中的继承的时候,一定会遇到原型,原型链的问题,原型里面又有prototype,__proto__,constructor属性,讲到这儿,很多同学是不是都一头雾水,傻傻分不清楚,因为工作中用到的地方是少之又少,再加上es6又出了extends语法糖,更加不用理会之,但是对于理解继承,原型和原型链是很重要的,理解函数和对象,理解prototype和__proto__,construct之间的关系尤为重要,不过本文对继承不予以深究,另起一篇文章写之,今天我们只讨论js中的原型和原型链。
首先,容在下提出一个问题。
到底prototype和__proto__是不是指同一个东西呢?
答案自然非也。
还有一个问题,就是ie8,9下是没有__proto__的概念的,如何解决这个问题?
这个问题在这篇文章结束之前会说明。
现在我们先来分析js中的对象。
js中对象是很重要的,所谓万物皆对象,但是js中对象分为两种,普通对象和函数对象
普通对象和函数对象
先来看几个例子
function f1(){};
var f2 = function(){};
var f3 = new Function('str','console.log(str)');
var o3 = new f1();
var o1 = {};
var o2 = new Object();
console.log(typeof Object); //function
console.log(typeof Function); //function
console.log(typeof o1); //object
console.log(typeof o2); //object
console.log(typeof o3); //object
console.log(typeof f1); //function
console.log(typeof f2); //function
console.log(typeof f3); //function
在上面的例子中,o1 o2 o3 为普通对象,f1 f2 f3 为函数对象。
那么怎么区分普通对象和函数对象呢?
其实很简单,凡是通过new Function()创建的对象都是函数对象,其他的都是普通对象。f1,f2,归根结底都是通过 new Function()的方式进行创建的。Function Object 也都是通过 New Function()创建的。
原型对象
接下来先说一下原型对象
在js中,每当定义一个对象的时候,对象中都会包含一些预定义的属性。其中函数对象的一个属性就是原型对象prototype。
普通对象没有prototype,只有__proto__属性,看下面的例子
function f1(){};
console.log(f1.prototype) //{constructor:ƒ f1(),__proto__:Object}
console.log(typeof f1.prototype) //Object
console.log(typeof Function.prototype) // Function,这个特殊,因为Function是通过new Function创建的
console.log(typeof Object.prototype) // Object
console.log(typeof Function.prototype.prototype) //undefined
从console.log(f1.prototype) //{constructor:ƒ f1(),__proto__:Object}可以看出f1.prototype就是f1的一个实例对象,就是在创建f1的时候,创建了一个它的实例对象,并把它赋给了prototype原型对象。代码如下
const temp = new f1();
f1.prototype = temp;
那么看到这儿,大家肯定会说,为什么要有原型对象?这个原型对象有什么用?
刚开始我就提到了,继承里会用到。看下下面的代码:
function Cat(name){
this.name = name;
}
Cat.prototype.getName = function(){
alert(this.name);
}
const qqq = new Cat('qqq');
qqq.getName();//qqq
从上面的代码中可以看出,通过给Cat的原型对象添加属性方法,那么Cat的实例都会拥有这个方法并可以调用之。有同学可能会有疑问,为什么在原型对象上添加了属性方法,它的实例就也可以拥有这个方法呢?这就牵扯到接下来说到的原型链了。
原型链
首先,js的对象(普通对象和函数对象)都会有__proto__属性,指向创建它的构造函数的原型对象,比如上面的例子
qqq.__proto__ === Cat.prototype;//true
Cat.prototype.__proto__ === Object.prototype;//true
Object.prototype.__proto__//null
这就形成了原型链,会一直查找原型对象的__proto__属性,直到为null。
有几个比较特殊的例子,来看一下
1.Object.__proto__ === Function.prototype // true
Object是函数对象,是通过new Function()创建,所以Object.__proto__指向Function.prototype。
2.Function.__proto__ === Function.prototype // true
Function 也是对象函数,也是通过new Function()创建,所以Function.__proto__指向Function.prototype。
3.Function.prototype.__proto__ === Object.prototype //true
Function.prototype是个函数对象,理论上他的__proto__应该指向 Function.prototype,就是他自己,自己指向自己,没有意义。
JS一直强调万物皆对象,函数对象也是对象,给他认个祖宗,指向Object.prototype。Object.prototype.__proto__ === null,保证原型链能够正常结束。
constructor
constructor是这么定义的。
在 Javascript 语言中,constructor 属性是专门为 function 而设计的,它存在于每一个 function 的prototype 属性中。这个 constructor 保存了指向 function 的一个引用。
Cat.prototype.constructor === Cat //true
Function.prototype.constructor === Function //true
Object.prototype.constructor === Object //true
这里也有要注意的
1.注意Object.constructor===Function;//true 本身Object就是Function函数构造出来的
2.如何查找一个对象的constructor,就是在该对象的原型链上寻找碰到的第一个constructor属性所指向的对象
总结
1.原型和原型链是实现继承的一种方式
2.原型链真正的继承是靠__proto__,而不是prototype,且看以下这个例子
var animal = function(name){
this.name = name;
}
var cat = function(){};
animal.say = 'lalala';
cat.prototype = animal;
var ca = new cat();
console.log(cat.say);//undefined
console.log(ca.say);//lalala
从输出结果可以看出,虽然cat的prototype指向了animal,但是读取say属性的时候并不会根据prototype找,ca本身虽然也没有say属性,但是看下面这段代码
ca.__proto__ = cat.prototype
cat.prototype = animal
所以ca.say输出lalala
3.之前遗留的问题,关于兼容ie的__proto__
ie9有Object.getPrototypeof()方法
function a(){console.log("aaa")};
const b = new a();
Object.getPrototypeof(b) === b.__proto__//true
ie8不支持Object.getPrototypeof方法,可以结合constructor和prototype
function a(){console.log("aaa")};
const b = new a();
b.constructor.prototype === b.__proto__//true
最后再思考下new()过程都做了些什么?(比如new A())
- 创建新对象var obj = {};
- 将新对象的construct属性指向构造函数,__proto__属性指向构造函数的prototype
- 执行构造函数,A.call(obj),将this指向obj
- 返回新对象(注意:如果构造函数返回this,基本类型或者不返回,就是返回新对象,如果返回引用类型,就是返回引用类型)
好了,今天就先写这么多,明天结合今天的原型和原型链总结下继承以及每个继承的优缺点~~~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。