原型链总结

113 阅读3分钟

理解原型

js分为函数对和普通对象,每个对象都有__proto__属性,但是只有函数对象(非箭头函数)才有prototype属性。

创建一个函数(非箭头函数),就会按照特定的规则为这个函数创建一个 prototype 属性(指向原型对象)。默认情况下,所有原型对象自动获得一个名为 constructor 的属性,指回与之关联的构造函数。

XXX.prototype 存储了 XXX 对象的共同属性,这样做的好处是:无需重复声明共有属性

function Dog(name) {
  this.name = name
}

Dog.prototype.wangwang = function(){ console.log('汪汪') }
Dog.prototype.run = function(){ console.log('狗在跑') }

let dog = new Dog('小白')
dog.run() // 狗在跑
dog.wangwang() // 汪汪

console.log(Dog.prototype.constructor === Dog) // true

__proto__prototype

原型与共有属性的关系:x.prototype:... 原型的地址

 X.__proto__ === 其构造函数的.prototype

javascript 的继承

一个对象系统的继承特性有三种实现方案,包括基于类、基于原型和基于元类。其中,JS使用了原型继承来实现对象系统。

早期的Javascript中并没有类,只是用 constructor 来实现类的某些功能。知道ES6才开始正式支持类,在语法上,类Class是一段静态描述,它必须先被确定好,然后由引擎去装载和装配(而在实现时,Javascript的类继承系统仍然是基于原型的————这是一种罕见的实现模型)。

不用 class 实现继承


 function Animal(color){
     this.color = color
 }
 Animal.prototype.move = function(){} // 动物可以动
 function Dog(color, name){
     Animal.call(this, color) // 或者 Animal.apply(this, arguments)
     this.name = name
 }
 // 下面三行实现 Dog.prototype.__proto__ = Animal.prototype
 function temp(){}
 temp.prototype = Animal.prototype
 Dog.prototype = new temp()

 Dog.prototype.constuctor = Dog 
 Dog.prototype.say = function(){ console.log('汪')}

使用类继承体系

ES6中,class可以使用继承

super的出现是为了填补原型继承的一项不足:无法有效调用父类方法。

class Book {
  constructor(title, pages, isbn) {
    this.title = title;
    this.pages = pages;
    this.isbn = isbn;
  }
  
  printIsbn() {
    console.log(this.isbn);
  }
}

class ITBook extends Book { 
  constructor (title, pages, isbn, technology) {
    // 通过 super 关键字引用父类的构造函数
    super(title, pages, isbn); 
    this.technology = technology;
  }

  printTechnology() {
    console.log(this.technology);
  }
}

let jsBook = new ITBook('学习 JS 算法', '200', '1234567890', 'JavaScript');
console.log(jsBook.title);
console.log(jsBook.printTechnology());

当父类(super)是内置的Object()时,extends Object可以省略 当不需要指定构造过程时,construct(){...}声明也可以省略

对于使用class关键字的类声明过程来说,最明显的收益就是可以使用extends关键字来声明父类

class MyObjectEx extends MyObject {
  ...
}

// 这基本上替代了构造函数声明风格的如下代码
MyObjectEx.prototype = new MyObject()
MyObjectEx.prototype.constructor = MyObjectEx

类是静态的声明

类是静态的声明,意味着类继承关系的构建过程也是静态的,是在语法分析期就决定了的。与此相关的,这也意味着类声明语法中的方法或属性存储器只是(对象方法的)声明,而不是函数,因此也就不能在声明内直接引用它们的名字。

//使用构造器声明
function MyObject() {
  //作为构造器,这里可以直接引用 MyObject 这个函数名
  console.log(typeof MyObject)
}

//使用类声明语法
class MyObjectEx {
  constructor() {
    console.log(typeof constructor) //undefined
  }
  foo() {
    console.log(typeof foo) //undefined
  }
}

混淆知识点

  • window是谁构造的?

Window,可以通过constructor属性看出构造者(window.constructor)
window.__proto__ === Window.Prototype

  • window.Object 是谁构造的?

window.Function
因为所有的函数都是window.Function构造的 window.Object.constructor === window.Function

  • window.Function 是谁构造的?

window.Function,浏览器构造了Function,然后指定它的构造者是自己
window.Function.constructor === window.Function

拨乱反正

一、xxx的原型

  • Object的原型是Object.__proto__  对
  • Object的原型是Object.prototype 错

二、Object.prototype 是根对象

1650292650(1).jpg

image.png

总结

  1. 对象.__proto__ === 其构造函数.prototype
  2. Object.prototype 是所有对象的(直接或间接)原型
  3. 所有函数都是由 Function 构造的 任意函数.__proto__ = Function.prototype