阅读 172

JavaScript原型与原型链

前言

本文主要围绕下述代码进行讲解,Person是构造函数,pPerson实例化的对象。

function Person(name) {
  this.name = name
}
let p = new Person('瑾行')
复制代码

动图预警

ezgif.com-gif-maker.gif

下面一起来逐步拆解GIF动画,完成最后的连线。🤳

显示原型

prototype称作显式原型,每一个函数(除箭头函数)在创建后都拥有prototype的属性,指向函数的原型对象。

prototype的设计之初是为了实现继承,让构造函数创建的实例共享共有的属性和方法,无需重复创建。

function Person(name) {
  this.name = name
}
Person.prototype.getName = function() {
    console.log(this.name)
}
let p = new Person('瑾行')
let p1 = new Person('七金')
p.getName() // 瑾行
p1.getName() // 七金
复制代码

理解了,就把prototype相关的线先连起来。ObjectFunction均是Javascript内置的对象,后面细讲。

image.png

constructor属性

存在每个函数(除箭头函数)显示原型中,是当前函数的引用。

打印下对象pconstructor属性。

console.log(p.constructor) // f Person() {}
复制代码

image.png
意料之中,是Person构造函数。但注意p本身没有constructor属性,是从原型上继承的,所以不需画线。
我们都知道Javascript中函数也是对象,那么打印下Personconstructor属性。

console.log(Person.constructor) // ƒ Function() { [native code] }
复制代码

从打印结果可以知道Person函数的构造函数是Function。敲黑板!知识点:我们所有的函数都是根据new Function()生成的。

new Function ([arg1[, arg2[, ...argN]],] functionBody)

function Person(name) {
  this.name = name
}
// 等价于
let Person = new Function(name, 'this.name = name')
复制代码

根据知识点,打印ObjectFunctionconstructor属性。

console.log(Object.constructor, Function.constructor)
// ƒ Function() { [native code] } ƒ Function() { [native code] }
复制代码

根据打印结果:Function是所有函数的构造函数(包括自己),且所有函数的constructor均继承于Function.prototype,同样无需画线。
还有一种情况,函数的显式原型。根据定义,毋庸置疑,constructor指向自身函数。

console.log(Person.prototype.constructor) // ƒ Person() {}
复制代码

同理可得出Object.prototype.constructorFunction.prototype.constructor

懂了的话,按讲解顺序连上与constructor相关的线。

image.png

隐式原型

__proto__称作隐式原型,所有的对象都具有__proto__属性。对象的隐式原型指向了该对象构造函数的原型对象,保证对象能访问挂载在原型链上定义的属性和方法。

pPerson构造函数实例化的对象,所以p.__proto__ === Person.prototypetrue,保证p可访问Person原型上定义的属性和方法。

console.log(p.__proto__ === Person.prototype) // true
复制代码

函数也是对象,上 1 小节了解到Function是所有函数的构造函数

console.log(Person.__proto__ === Function.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true
console.log(Function.__proto__ === Function.prototype) // true
复制代码

函数的显示原型也是对象,是Object构造函数产生。

console.log(Person.prototype.__proto__ === Object.prototype) // true
console.log(Function.prototype.__proto__ === Object.prototype) // true
复制代码

构造函数Person的显式原型也是对象,所以我们尝试打印下它的隐式原型。 现在,我们可以得出结论:所有的对象都继承于Object.prototype。
不信的话,打印下Object.prototype.__proto__

console.log(Object.prototype.__proto__) // null
复制代码

v2-b3a0fd7362a806434943c92d0d250d25_720w.jpg

现在,让我们把剩下关于__proto__的线连好,圆满完成!!!

image.png

原型链

如果看到这里,你应该知道原型链由__proto__将对象和原型连接起来组成原型链✌。

面试高频问题

题目:instanceofisPrototypeOf有何区别?

instanceOf:检测构造函数prototype是否出现在某个实例对象的原型链上。
isPrototypeOf:检查原型对象是否出现在某个实例对象的原型链上。

function Person() {}
let p = new Person()

console.log(Person.prototype.isPrototypeOf(p) // true
console.log(p instanceof Person) // true
复制代码

根据定义和代码可以的得出结论:两者具备相同的作用,但主要目标对象不同,instanceOf目标对象是构造函数,isPrototypeof则是原型对象。

题目:手写实现instanceof

function instanceof(target, origin) {
  if(typeof target !== 'object' || origin === null) return false
  if(target.__proto__ === origin.prototype) return true
  else {
    return instanceof(target.__proto__, origin)
  }
}
复制代码

总结

如果读到这里,我相信你至少会用画图地形式去描述原型与原型链,那就毫不吝啬地点个赞吧🤞。

文章分类
前端
文章标签