资讯专栏INFORMATION COLUMN

这样理解原型与原型链比较简单

csRyan / 3462人阅读

摘要:属性这是每个对象都有的隐式原型属性,指向了创建该对象的构造函数的原型。

原型

在JavaScript中,有两个原型,分别是 prototype_proto_
注:在ECMA-262第5版中管这个 _proto_ 叫 [[Prototype]]

prototype 属性:
这是一个显式原型属性,只有函数才拥有该属性。
_proto_ 属性:
这是每个对象都有的隐式原型属性,指向了创建该对象的构造函数的原型。

什么是函数?什么是对象?

创建函数有两种方式:
①、通过 function 关键字定义
②、通过 new Function
其中函数又可以分为普通函数和构造函数,两者唯一的区别就是调用的方式不同,语法上没有差异

function normalFn(){}   // 普通函数(函数名首字母小写)
function StructFn(){}   // 构造函数(函数名首字母大写)

创建对象的方式多种多样,其中传统方法是通过构造函数来创建对象,使用 new 关键字即可创建,创建出来的对象实例可通过 constructor 来访问构造函数

let obj = new StructFn()  // obj就是一个对象
console.log(obj.constructor) // function normalFn(){}

在JS中,万物都是对象,函数也属于对象,只不过函数相对于对象有着更为精确的定义

原型初探

为了证明 prototype属性 是函数独有,而__proto__是每个对象都有的,我们可以测试以下代码:

function a(){}
console.log(a.prototype);    // {constructor: ƒ}
console.log(a.__proto__);    // ƒ () { [native code] }

let obj = new Object()
console.log(obj.prototype)  // undefined
console.log(obj.__proto__)  // {constructor: ƒ, __defineGetter__: ƒ,  …}

可以看到对象的 显式原型(prototype)为undefined

*注意:
undefinednull 同属于对象,但是它们都没有原型,为什么? 在JavaScript中,目前只有两个只有一个值的数据类型,那就是 undefined 和 null*
由于这两种数据类型有且只有一个值,并且没有方法,所以自然而然就没有原型了。
有的同学会问,那NaN呢?NaN是属于Number数据类型的,属于对象,只有_proto_ 属性
console.log(undefined.__proto__);  // 报错:Uncaught TypeError: Cannot read property "__proto__" of undefined
console.log(null.__proto__);       // 报错:Uncaught TypeError: Cannot read property "__proto__" of null
console.log(NaN.__proto__) ;       // Number {0, constructor: ƒ, toExponential: ƒ,  …}
构造函数创建对象,其中发生什么?
1.创建了一个新对象
2.将新创建的对象的隐式原型指向其构造函数的显式原型。
3.将this指向这个新对象
4.返回新对象

注意看第二条:将新创建的对象的隐式原型指向其构造函数的显式原型
也就是说 对象.__prototype === 构造函数.prototype

function fn(){}
let obj = new fn();
console.log(obj.__proto__ === fn.prototype);  // true

那么这样,我们在为构造函数添加原型方法时,就可以通过两种方法取访问了

fn.prototype.more = function(){console.log("fn-prototype-more")}

// 1、通过构造函数的显式原型
fn.prototype.more()

// 2、通过对象的隐式原型
obj.__proto__.more()
原型链

原型链:实例与原型之间的链接

先来看一个例子:

function Abc(){}
Abc.prototype.fn = function(){console.log("Abc-prototype-fn")};
let obj = new Abc()
obj.fn() // Abc-prototype-fn

从上面的代码我们可以看到。构造函数 Abc 和对象实例 obj 都没有fn这个方法,之所以对象obj能够访问到Abc的原型方法,是通过原型链来寻找,借用了 _proto_ 这个属性

所以以下的代码也是等价的

obj.fn()             // Abc-prototype-fn
obj.__proto__.fn()   // Abc-prototype-fn

再来看一个复杂的例子:

function Abc(){
    this.fn = function(){console.log("Abc-fn")};
}
Abc.prototype.fn = function(){console.log("Abc-prototype-fn")};
Object.prototype.fn = function(){console.log("Object-fn")};

let obj = new Abc()
obj.fn = function(){console.log("obj-fn")};
obj.fn()

这里有4个重名的fn函数,分别是:
①、实例对象obj的方法
②、构造函数Abc的方法
③、构造函数Abc原型上的方法
④、Obecjt对象原型上的方法

为了表示方法的寻找过程,我画了一幅很丑陋的图,大家不要介意哈!

在寻找某个方法或者属性的时候,会先从自身对象寻找;
如果没有,则会去构造函数寻找;注意这里还没用到原型链;
紧接着,会到构造函数的原型上寻找,此时就是对象Abc通过 _proto_ 属性进行寻找
接下来,会到Object对象的原型寻找,Object对象是所有对象之父,可以说所有的对象都继承了Object,此时构造函数通过_proto_ 属性找到了Object的prototype
最后的最后,由于Object._proto_ 指向了null,这也就是原型链的末端


第一道原型链是 obj._proto_ ,访问到了Abc的prototype

console.log(obj.__proto__ === Abc.prototype)  // true

第二道原型链是 obj.__proto__.__proto__ ,访问到了Object的prototype

console.log(obj.__proto__.__proto__ === Object.prototype) //true

第三道原型链是 obj.__proto__.__proto__.__proto__ 访问到了Object的prototype.__proto__,,最后指向了null

console.log(obj.__proto__.__proto__.__proto__ === null) //true

这样我们就可以看到了整个原型链的流程了

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101531.html

相关文章

  • 详解js中的继承(二)

    摘要:寄生组合式继承终于写到最后一个继承了,我们在之前讲了种继承方式,分别是原型链,借用构造函数继承,组合继承,原型式继承,寄生式继承,其中,前三种联系比较紧密,后面两种也比较紧密,而我们要讲的最后一种,是和组合继承还有寄生式继承有关系的。 前言 趁周末结束之前赶紧先把坑填上。上回我们说到了原型链,并且留下了几个思考题,先把答案公布一下。 在最后一个例子里,console.log(b1.c...

    evin2016 评论0 收藏0
  • 前端基础进阶(九):详解面向对象、构造函数、原型原型

    摘要:我们通过一个简单的例子与图示,来了解构造函数,实例与原型三者之间的关系。而原型对象的指向构造函数。于是根据构造函数与原型的特性,我们就可以将在构造函数中,通过声明的属性与方法称为私有变量与方法,它们被当前被某一个实例对象所独有。 showImg(https://segmentfault.com/img/remote/1460000008593382); 如果要我总结一下学习前端以来我遇...

    Tony_Zby 评论0 收藏0
  • 我所认识的JavaScript作用域原型

    摘要:为了防止之后自己又开始模糊,所以自己来总结一下中关于作用域链和原型链的知识,并将二者相比较看待进一步加深理解。因此我们发现当多个作用域相互嵌套的时候,就形成了作用域链。原型链原型说完了作用域链,我们来讲讲原型链。   毕业也整整一年了,看着很多学弟都毕业了,忽然心中颇有感慨,时间一去不复还呀。记得从去年这个时候接触到JavaScript,从一开始就很喜欢这门语言,当时迷迷糊糊看完了《J...

    Bmob 评论0 收藏0
  • 深入学习js之——原型原型

    摘要:我们用一张图表示构造函数和实例原型之间的关系好了构造函数和实例原型之间的关系我们已经梳理清楚了,那我们怎么表示实例与实例原型,也就是或者和之间的关系呢。 开篇: 在Brendan Eich大神为JavaScript设计面向对象系统的时候,借鉴了Self 和Smalltalk这两门基于原型的语言,之所以选择基于原型的面向对象系统,并不是因为时间匆忙,它设计起来相对简单,而是因为从一开始B...

    FingerLiu 评论0 收藏0
  • 深入学习js之——原型原型

    摘要:我们用一张图表示构造函数和实例原型之间的关系好了构造函数和实例原型之间的关系我们已经梳理清楚了,那我们怎么表示实例与实例原型,也就是或者和之间的关系呢。 开篇: 在Brendan Eich大神为JavaScript设计面向对象系统的时候,借鉴了Self 和Smalltalk这两门基于原型的语言,之所以选择基于原型的面向对象系统,并不是因为时间匆忙,它设计起来相对简单,而是因为从一开始B...

    xialong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<