美文网首页
彻底搞懂原型及原型链

彻底搞懂原型及原型链

作者: moidream | 来源:发表于2019-04-03 19:10 被阅读0次

前言

说起原型链一直以来知道的都是沿着原型链向上找某个方法或属性。但是具体是怎么向上找的一直没有去搞清楚过。

这次通过Animal, Dog 和 Hashiqi三个构造函数, 来彻底搞懂原型及原型链。 并通过原型和原型链去了解一下Javascript内部的结构

如有建议,欢迎留言。
原网页

一、 术语

1.1 构造函数

如名称所示,构造函数是一个函数, 用来创建实例。

Javascript内置有一些原生构造函数,如Object, Array, Function等。 也可以创建自己的构造函数,约定命名以大写字母开头,如下面将说到的Animal, Dog 和 Hashiqi。

通过new操作符调用构造函数,及可创建出一个对应的实例出来。

1.2 实例

由构造函数创建出来的具体“对象”,比如我们可以由Dog这个构造函数创建出来一只具体的blackDog出来。

1.3 原型对象

每一个构造函数都有一个prototype属性,指向其原型对象。该原型对象包含可以由特定类型所有实例共享的属性和方法。

注意:箭头函数没有prototype属性,ES6规定箭头函数不可做为构造函数用new操作符调用,否则会抛出错误。

除此之外每一个实例包含一个[[Prototype]]属性,指向该实例构造函数的原型对象。[[Prototype]]无法直接访问,一般浏览器将该属性实现为proto。这个属性是实现原型链的关键。

二、 通过具体示例分析原型和原型链

2.1 创建三个构造函数


function Animal (legNum, color) {

  this.color = color

  this.legNum = legNum

}

Animal.prototype.eat = function() {

  console.log('吃')

}

Animal.prototype.move = function() {

  console.log('动')

}

function Dog (legNum, color, skill) {

  Animal.call(this, legNum, color)

  this.skill = skill

}

Dog.prototype = Object.create(Animal.prototype)

Dog.prototype.say = function () {

  console.log('汪汪')

}

Dog.prototype.constructor = Dog

function Hashiqi () {

  Dog.apply(this, arguments)

}

Hashiqi.prototype = Object.create(Dog.prototype)

Hashiqi.prototype.constructor = Hashiqi

现在有了三个构造函数,Animal、 Dog 和 Hashiqi。Animal是Dog的父类,Dog是Hashiqi的父类,符合日常生活经验。

2.2 创建一个哈士奇实例来分析原型和原型链


var erHa = var erHa = new Hashiqi(4, '灰', '拆家')

console.log(erHa)

// Hashiqi { color: '黑', legNum: 4, skill: '拆家' }

// 现在有了一只四条腿颜色为灰色会拆家的二哈,拿它来具体分析一下

erHa.say() // 汪汪

erHa.move() // 动

console.log(erHa.__proto__)

console.log(erHa.__proto__ === Hashiqi.prototype)

// Hashiqi { constructor: [Function: Hashiqi] }

// true

console.log(Dog.prototype)

// Dog { say: [Function], constructor: [Function: Dog] }

console.log(Animal.prototype)

// Animal { eat: [Function], move: [Function] }

通过上面代码及打印结果可以看到,erHa实例自身的属性仅有color、legNum和skill(可以通过hasOwnProperty方法验证)。 但是调用say和move方法却可以调用成功。say方法来自于Dog的原型对象,move方法来自Animal的原型对象。符合一直以来知道的知识,实例上找不到的方法或属性会沿着原型链向上找。

接下来继续分析具体是如何沿着原型链向上找的。


console.log(erHa.__proto__) // Hashiqi.prototype

console.log(erHa.__proto__.__proto__) // Dog.prototype

console.log(erHa.__proto__.__proto__.__proto__) // Animal.prototype

console.log(erHa.__proto__.__proto__.__proto__.__proto__) // Object.prototype

console.log(erHa.__proto__.__proto__.__proto__.__proto__.__proto__) // null

// erHa.__proto__.__proto__.__proto__.__proto__.__proto__很像一条长长的链子

通过上面打印结果就可以比较清晰的看到原型链的具体逻辑:

erHa实例的proto属性指向Hashiqi的原型对象,

Hashiqi原型对象的proto属性指向Dog的原型对象,

Dog原型对象的proto属性指向Animal的原型对象,

Animal原型对象的proto属性指向Object的原型对象,

Object原型对象的proto属性最终指向null。

原型链

2.3 注意项

原型链需要正确的继承方法才可生成,错误的继承方式会导致原型链截断

现在修改Hashiqi构造函数来演示一下


function Hashiqi () {

  Dog.apply(this, arguments)

}

Hashiqi.prototype = Object.assign({}, Dog.prototype)

Hashiqi.prototype.constructor = Hashiqi

console.log(erHa.__proto__) // Hashiqi.prototype

console.log(erHa.__proto__.__proto__) // Object.prototype

erHa.move() // error

如上所示,直接通过Object.assign拷贝父类原型的方式实现继承,会导致原型链断掉。Hashiqi此时的原型对象直接为Object的实例。

三、 了解Javascript内部结构


console.log(Object.prototype.__proto__) // null

console.log(Object.prototype)

console.log(Function.prototype.__proto__ === Object.prototype)

console.log(Array.prototype.__proto__ === Object.prototype)

console.log(Number.prototype.__proto__ === Object.prototype)

console.log(String.prototype.__proto__ === Object.prototype)

console.log(Boolean.prototype.__proto__ === Object.prototype)

console.log(RegExp.prototype.__proto__ === Object.prototype)

console.log(RegExp.prototype.__proto__ === Object.prototype)

console.log(Symbol.prototype.__proto__ === Object.prototype)

console.log(Object.__proto__ === Function.prototype)

console.log(Function.__proto__ === Function.prototype)

console.log(Array.__proto__ === Function.prototype)

console.log(Number.__proto__ === Function.prototype)

console.log(String.__proto__ === Function.prototype)

console.log(Boolean.__proto__ === Function.prototype)

console.log(RegExp.__proto__ === Function.prototype)

console.log(Symbol.__proto__ === Function.prototype)

console.log(Function instanceof Object)

console.log(Object instanceof Function)

尝试运行一下上面代码,可以发现Javascript原型链顶层为null,这也可以解释为何typeof null结果为objcet。

null之后是Object.prototype,Javascript世界中一切对象皆可调用其上的方法(没有被子类改写的话)。

其次比较特殊的是Function构造函数,Javascript世界中所有构造函数均由其生成。与Objcet构造函数是一个蛋生鸡鸡生蛋的关系。

四 undefined

最后可以思考一下undefined在Javascript世界中所处的角色是什么。

比如这里应该有内容但是现在并没有...

相关文章

  • 彻底搞懂原型及原型链

    前言 说起原型链一直以来知道的都是沿着原型链向上找某个方法或属性。但是具体是怎么向上找的一直没有去搞清楚过。 这次...

  • JS中的原型和原型链详解

    JS中的原型和原型链是大家彻底搞懂JS面向对象及JS中继承相关知识模块非常重要的一个模块,一旦突破这块知识点,相信...

  • lesson 5 面向对象及原型链 2021-04-29

    课程标题 面向对象及原型链 课程目标 面向对象思想 原型及原型链 继承 知识点 面向对象思想 原型链的指向 new...

  • 一分钟秒杀,js原型与原型链

    有人说作者吹牛逼,我特么几年没搞懂的东西。一分钟理解原型,原型链?扯犊子吧?说实话,原型和原型链这个东西,无非就是...

  • 2017-12-04

    一个例子让你彻底明白原型对象和原型链

  • js_继承及原型链等(四)

    js_继承及原型链等(三) 1. 继承 依赖于原型链来完成的继承 发生在对象与对象之间 原型链,如下: ==原型链...

  • 彻底搞懂JavaScript原型

    开局先放一张图,接下来我们可以对照这张图来理解下面的内容。 1. 原型 1.1 引用类型 所有的引用类型(数组、对...

  • JavaScript实现继承的几种方式及其优缺点

    原型链 要搞懂JavaScript的继承,首先要先理解JavaScript的原型链。 每一个实例对象都有一个pro...

  • 原型及原型链

    使用对象---->使用对象中的属性和和对象中的方法,使用对象就要先有构造函数 构造函数 原型链:是一种关系,实例对...

  • 原型及原型链

网友评论

      本文标题:彻底搞懂原型及原型链

      本文链接:https://www.haomeiwen.com/subject/hoqtiqtx.html