原型与原型链相关

2019-08-02  本文已影响0人  你喜欢吃青椒吗_c744

前言

最近在整理原生JS的相关知识,发现关于原型和原型链涉及的知识对理解JS有很大的帮助。遂分享一下。

构造函数

所谓构造函数,就是提供了一个生成对象的模板并描述对象的基本结构的函数。一个构造函数,可以生成多个对象,每个对象都有相同的结构。总的来说,构造函数就是对象的模板,对象就是构造函数的实例。

构造函数的缺点

所有的实例对象都可以继承构造函数中的属性和方法。但是,同一个对象实例之间,无法共享属性

function Person(name,height){
 this.name=name;
 this.height=height;
 this.hobby=function(){
 return 'watching movies';
}
 }
var boy=new Person('keith',180);
 var girl=new Person('rascal',153);
 console.log(boy.name); //'keith'
 console.log(girl.name); //'rascal'
 console.log(boy.hobby===girl.hobby); //false

上面代码中,一个构造函数Person生成了两个对象实例boy和girl,并且有两个属性和一个方法。但是,它们的hobby方法是不一样的。也就是说,每当你使用new来调用构造函数放回一个对象实例的时候,都会创建一个hobby方法。这既没有必要,又浪费资源,完全可以被两个对象实例共享。
所以,构造函数的缺点就是:同一个构造函数的对象实例之间无法共享属性或方法

prototype解决这个问题

为了解决构造函数的对象实例之间无法共享属性的缺点,js提供了prototype属性。

js中每个数据类型都是对象(除了null和undefined),而每个对象都继承自另外一个对象,后者称为“原型”(prototype)对象,只有null除外,它没有自己的原型对象。

原型对象上的所有属性和方法,都会被对象实例所共享。

function Person(name,height){
this.name=name;
this.height=height;
}
Person.prototype.hobby=function(){
return 'watching movies';
}
var boy=new Person('keith',180);
var girl=new Person('rascal',153);
console.log(boy.name); //'keith'
console.log(girl.name); //'rascal'
console.log(boy.hobby===girl.hobby); //true
//将hobby方法放在原型对象上,那么两个实例对象都共享着同一个方法。

上面代码中,当修改了原型对象的hobby方法之后,两个对象实例都发生了变化。这是因为对象实例其实是没有hobby方法,都是读取原型对象的hobby方法。也就是说,当某个对象实例没有该属性和方法时,就会到原型对象上去查找。如果实例对象自身有某个属性或方法,就不会去原型对象上查找

boy.hobby=function(){
 return 'play basketball';
 }
 console.log(boy.hobby()); //'play basketball'
 console.log(girl.hobby()); //'swimming'
//上面代码中,boy对象实例的hobby方法修改时,就不会在继承原型对象上的hobby方法了。不过girl仍然会继承原型对象的方法。

总结:

原型链

对象的属性和方法,有可能是定义在自身,也有可能是定义在它的原型对象。由于原型对象本身对于对象实例来说也是对象,它也有自己的原型,所以形成了一条原型链(prototype chain)。

比如,a对象是b对象的原型,b对象是c对象的原型,
以此类推。所有一切的对象的原型顶端,都是Object.prototype,
即Object构造函数的prototype属性指向的那个对象。

当然,Object.prototype对象也有自己的原型对象,那就是没有任何属性和方法的null对象,而null对象没有自己的原型。即Object.prototype = null

constructor

prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。

由于constructor属性是定义在原型(prototype)对象上面,意味着可以被所有实例对象继承。

function A(){};
var a=new A();
console.log(a.constructor); //A()
console.log(a.constructor===A.prototype.constructor);//true
//之所以可以a.constructor是因为A的原型对象有这个属性;
//而a会继承原型对象的属性和方法,
//所以,a才可以调用constructor属性。

constructor属性的作用

function A(){};
var a=new A();
console.log(a.constructor===A) //true
console.log(a.constructor===Array) //false

instanceof运算符

instanceof运算符返回一个布尔值,表示指定对象是否为某个构造函数的实例。

因为instanceof对整个原型链上的对象都有效,所以同一个实例对象,可能会对多个构造函数都返回true。

注意,instanceof对象只能用于复杂数据类型(数组,对象等),不能用于简单数据类型(布尔值,数字,字符串等)。此外,null和undefined都不是对象,所以instanceof 总是返回false。

_ proto _

JS的万物都有__proto__属性。用于指向创建它的构造函数的原型对象。

为什么不用prototype指向原型?
因为prototype属性只有函数对象才拥有。

image.png
//创建一个构造函数
function People (name) {
  this.name = name;
}
//实例化一个构造函数
let g1 = new People('蜡笔小新')

那么,则会有:

People.prototype === g1.__proto__
《JavaScript 高级程序设计》的图 6-1

[[Prototype]] 其实就是__proto__。ECMA-262第5版中管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但Firefox,Safari和Chrome在每个对象上都支持一个属性__proto__;而在其他实现中,这个属性对脚本是完全不可见的。

Object和Function

Object对象体系下:
一切对象的原型链最终都是.... → Object.prototype → null。例如定义一个num变量var num = 1,则num的原型链为x → Number.prototype → Object.prototype → null; 定义一个函数对象fnfunction fn() {},则fn的原型链为fn → Function.prototype → Object.prototype → null;等等...

原型链

Function体系下:
不考虑null的情况下(其中Object.prototype.__proto__ === null),Object.prototype为原型链的顶端,Function.prototype继承Object.prototype而产生,最后,FunctionObject和其它构造函数继承Function.prototype而产生。

原型链
注意:Function.prototype === Function.__proto__true

JS万物诞生记

https://img.haomeiwen.com/i17237086/1333d91c5d466cea.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240

总结

参考文章

[详解Javascript中prototype属性(推荐)]

最详尽的 JS 原型与原型链终极详解,没有「可能是」。(一)
最详尽的 JS 原型与原型链终极详解,没有「可能是」。(二)
最详尽的 JS 原型与原型链终极详解,没有「可能是」。(三)
彻底搞懂Object和Function的关系

由一段代码引发的关于Object和Function的鸡和蛋问题的思考

JavaScript 世界万物诞生记

上一篇下一篇

猜你喜欢

热点阅读