zhangdizhangdi

原型、原型链、继承

原型、原型链

每个对象拥有一个原型对象。

当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

参考

构造函数

构造函数本身就是一个函数,与普通函数没有任何区别,不过为了规范一般将其首字母大写
构造函数和普通函数的区别在于,使用 new 生成实例的函数就是构造函数,直接调用的就是普通函数。

Object 实例的 __proto__ 访问器属性暴露了此对象的 [[Prototype]](一个对象或 null)。

原型

每个函数都有一个 prototype 属性,指向调用该构造函数而创建的实例的原型。每个对象都有一个隐式属性 __proto__(或 [[Prototype]]),指向它的原型对象。

  • 实例 通过 __proto__ 指向原型
  • 构造函数 通过 prototype 指向原型
  • 原型 通过 constructor 指回构造函数

原型链

当试图访问一个对象的属性时,如果对象本身没有,就会去它的 __proto__(原型)中找,如果还没有,就继续往原型的原型上找,直到找到 Object.prototype.__proto__(即 null)为止。这就是原型链。

方法

new

  1. 创建一个空的简单对象
  2. 新对象的 [[Prototype]] 指向构造函数的原型对象(prototype)
  3. 新对象作为 this 的上下文
  4. 执行构造函数内部的代码
  5. 如果函数没有返回其他对象,那么返回这个新对象
js
function _new() {
  // 1、获得构造函数,同时删除 arguments 中第一个参数
  const Constructor = [].shift.call(arguments)
  // 2、创建一个空的对象并链接到原型,obj 可以访问构造函数原型中的属性
  let obj = Object.create(Constructor.prototype)
  // 或者
  // let obj = new Object()
  // obj.__proto__ = Constructor.prototype

  // 3、绑定 this 实现继承,obj 可以访问到构造函数中的属性
  let ret = Constructor.apply(obj, arguments)
  // 4、优先返回构造函数返回的对象
  return ret instanceof Object ? ret : obj
}

Object.create

Object.create(null) 可以创建出没有原型&原型链的对象。

js
const _create = function (proto) {
  const Fn = function () {}
  Fn.prototype = proto
  return new Fn()
}

function A() {
  this.a = 'test'
}
const a1 = Object.create(A)
const a2 = _create(A)
const a3 = new A()

console.log(Object.getPrototypeOf(a1) === A) // true
console.log(Object.getPrototypeOf(a2) === A) // true

console.log(a1.a) // undefined
console.log(a2.a) // undefined
console.log(a3.a) // test

a1.a = 'a1a'
console.log(a1.a) // a1a
收藏

instanceof

js
function Test() {
  console.log('Test function')
  return 'ttt'
}
const t = new Test()
const t2 = Test() // Test function

console.log('t:', t) // Test { }
console.log('t2:', t2) // ttt
console.log('------------------')

console.log('t Test:', t instanceof Test) // true
console.log('t Object:', t instanceof Object) // true
console.log('t Function:', t instanceof Function) // false
console.log('Test Object:', Test instanceof Object) // true
console.log('Test Function:', Test instanceof Function) // true

console.log('------------------')

function _instanceof(target, Fn) {
  if (
    (typeof target !== 'object' && typeof target !== 'function') ||
    target === null
  )
    return false

  while (target) {
    if (target.__proto__ === Fn.prototype) {
      return true
    } else {
      target = target.__proto__
    }
  }
  return false
}

console.log('t Test:', _instanceof(t, Test)) // true
console.log('t Object:', _instanceof(t, Object)) // true
console.log('t Function:', _instanceof(t, Function)) // false
console.log('Test Object:', _instanceof(Test, Object)) // true
console.log('Test Function:', _instanceof(Test, Function)) // true
执行结果
Test function
Test function
t: {}
t2: ttt
------------------
t Test: true
t Object: true
t Function: false
Test Object: true
Test Function: true
------------------
t Test: true
t Object: true
t Function: false
Test Object: true
Test Function: true

题目

js
function A() {}
A.prototype.n = 1
// A.prototype = {
//   n: 1,
// }

const b = new A()

A.prototype = {
  n: 2,
  m: 3,
}

const c = new A()

console.log('b.__proto__', b.__proto__) // {n: 1}
console.log('b.__proto__.constructor', b.__proto__.constructor) // A
console.log('c.__proto__', c.__proto__) // {n: 2, m: 3}
console.log('c.__proto__.constructor', c.__proto__.constructor) // Object
console.log('A.prototype', A.prototype) // {n: 2, m: 3}
console.log(b.n, b.m, c.n, c.m) //1 undefined 2 3

A.prototype.n = 44
const d = new A()

console.log('b.__proto__', b.__proto__) // {n: 1}
console.log('c.__proto__', c.__proto__) // {n: 44, m: 3}
console.log('d.__proto__', d.__proto__) // {n: 44, m: 3}
console.log('A.prototype', A.prototype) // {n: 44, m: 3}
console.log(b.n, b.m, c.n, c.m) //1 undefined 44 3
执行结果
b.__proto__ {"n":1}
b.__proto__.constructor 
c.__proto__ {"n":2,"m":3}
c.__proto__.constructor 
A.prototype {"n":2,"m":3}
1 undefined 2 3
b.__proto__ {"n":1}
c.__proto__ {"n":44,"m":3}
d.__proto__ {"n":44,"m":3}
A.prototype {"n":44,"m":3}
1 undefined 44 3
js
function Fn() {
  var n = 10
  this.m = 20
  this.aa = function () {
    console.log(this.m)
  }
}

Fn.prototype.bb = function () {
  console.log('Fn.prototype.bb this:', this)
  console.log(this.n)
}

var f1 = new Fn()

Fn.prototype = {
  aa: function () {
    console.log('Fn.prototype.aa this:', this)
    console.log(this.m + 10)
  },
}

var f2 = new Fn()

console.log('f1:', f1)
console.log(f1.__proto__.constructor) // ==> function Fn(){...}
f1.bb() // undefined
f1.aa() // 20

console.log('------------------')

console.log('f2:', f2)
console.log(f2.__proto__.constructor) // ==> Object() { [native code] }
f2.aa() // 20
f2.__proto__.aa() // NaN
f2.bb() //  Uncaught TypeError: f2.bb is not a function
执行结果
f2.bb is not a function

new 返回的是 object 不是 function

js
function F() {}
Object.prototype.a = function () {
  console.log('a()')
}
Function.prototype.b = function () {
  console.log('b()')
}

var f = new F()

console.log(typeof f, typeof F) // object function
console.log('f', f instanceof Function) // false
console.log('F', F instanceof Function) // true

f.a() // a()
// f.b(); // Uncaught TypeError: f.b is not a function
F.a() // a()
F.b() // b()
js
function Foo() {
  Foo.a = function () {
    console.log(1)
  }
  this.a = function () {
    console.log(2)
  }
}
Foo.prototype.a = function () {
  console.log(3)
}
Foo.a = function () {
  console.log(4)
}

Foo.a() // 4

let obj = new Foo()
obj.a() // 2
Foo.a() // 1
js
function Foo() {
  getName = function () {
    console.log(1)
  }

  console.log('FOO', Foo)
  console.log('this', this)
  return this
}
Foo.getName = function () {
  console.log(2)
}
Foo.prototype.getName = function () {
  console.log(3)
}
var getName = function () {
  console.log(4)
}
function getName() {
  console.log(5)
}

Foo.getName() // 2
getName() // 4
Foo().getName() // 1
getName() // 1

console.log('------------------')

new Foo.getName() // 2
new Foo().getName() // 3
js
function Foo() {
  this.getName = function () {
    console.log(1)
  }

  console.log('FOO', Foo)
  console.log('this', this)
  return this
}
Foo.getName = function () {
  console.log(2)
}
Foo.prototype.getName = function () {
  console.log(3)
}
var getName = function () {
  console.log(4)
}
function getName() {
  console.log(5)
}

Foo.getName() // 2
getName() // 4
Foo().getName() // 1
getName() // 1

console.log('------------------')

new Foo.getName() // 2
new Foo().getName() // 1

继承

实现方法:
  1. 原型链继承
  2. 借用构造函数
  3. 组合继承
  4. 原型式继承
  5. 寄生式继承
  6. 寄生组合式继承
  7. ES6 Class 继承 extends

1. 原型链继承

js
function Parent() {
  this.name = 'parent'
  this.colors = ['red', 'blue', 'green']
}
function Child() {
  this.name = 'child'
}

Child.prototype = new Parent()

//

const c1 = new Child()
c1.name = 'child1'
c1.colors.push('black')
console.log(c1.name) // "child1"
console.log(c1.colors) // "red,blue,green,black"

const c2 = new Child()
console.log(c2.name) // "child"
console.log(c2.colors) // "red,blue,green,black"

2. 借用构造函数

js
function Parent() {
  this.colors = ['red', 'blue', 'green']
}
function Child() {
  Parent.call(this) // 继承父类属性
}

Child.prototype = new Parent()

//

const c1 = new Child()
c1.colors.push('black')
console.log(c1.colors) // "red,blue,green,black"

const c2 = new Child()
console.log(c2.colors) // "red,blue,green"

3. 组合继承

js
function Parent(name) {
  this.name = name
  this.colors = ['red', 'blue', 'green']
}

function Child(name, age) {
  Parent.call(this, name) // 继承父类属性
  this.age = age
}

Child.prototype = new Parent()
Child.prototype.constructor = Child

//

const c1 = new Child('child1', 10)
c1.colors.push('black')
console.log(c1)
console.log(c1.name) // "child1"
console.log(c1.age) // 10
console.log(c1.colors) // "['red', 'blue', 'green', 'black']"

console.log('---')

const c2 = new Child('child2', 20)
console.log(c2.name) // "child2"
console.log(c2.age) // 20
console.log(c2.colors) // "['red', 'blue', 'green']"

4. 原型式继承

js
const parent = {
  name: 'parent',
  colors: ['red', 'blue', 'green'],
}

const c1 = Object.create(parent)
const c2 = Object.create(parent)

//

c1.name = 'child1'
c1.colors.push('black')

console.log(c1)
console.log(c1.name) // "child1"
console.log(c1.colors) // "['red', 'blue', 'green', 'black']"

console.log('---')

c2.name = 'child2'
c2.colors.push('white')

console.log(c2)
console.log(c2.name) // "child2"
console.log(c2.colors) // "['red', 'blue', 'green', 'white']"

console.log('---')

console.log(parent)
console.log(parent.name) // "parent"
console.log(parent.colors) // "['red', 'blue', 'green', 'black', 'white']"

5. 寄生式继承

js
function createObj(o) {
  const clone = Object.create(o)
  clone.sayHi = function () {
    console.log('hi', this.name)
  }
  return clone
}

const parent = {
  name: 'parent',
  colors: ['red', 'blue', 'green'],
}

const c1 = createObj(parent)
const c2 = createObj(parent)

//

c1.name = 'child1'
c1.colors.push('black')

console.log(c1)
console.log(c1.name) // "child1"
console.log(c1.colors) // "['red', 'blue', 'green', 'black']"
c1.sayHi() // "hi child1"

console.log('---')

c2.name = 'child2'
c2.colors.push('white')

console.log(c2)
console.log(c2.name) // "child2"
console.log(c2.colors) // "['red', 'blue', 'green', 'white']"
c2.sayHi() // "hi child2"
console.log('---')

console.log(parent)
console.log(parent.name) // "parent"
console.log(parent.colors) // "['red', 'blue', 'green', 'black', 'white']"
parent.sayHi() // Uncaught TypeError: parent.sayHi is not a function

6. 寄生组合式继承

js
function Parent(name) {
  this.name = name
  this.colors = ['red', 'blue', 'green']
}

function Child(name, age) {
  Parent.call(this, name) // 继承父类属性
  this.age = age
}

function prototype(child, parent) {
  const prototype = Object.create(parent.prototype)
  prototype.constructor = child
  child.prototype = prototype
}

prototype(Child, Parent)

//

const c1 = new Child('child1', 10)
c1.colors.push('black')
console.log(c1)
console.log(c1.name) // "child1"
console.log(c1.age) // 10
console.log(c1.colors) // "['red', 'blue', 'green', 'black']"

console.log('---')

const c2 = new Child('child2', 20)
console.log(c2)
console.log(c2.name) // "child2"
console.log(c2.age) // 20
console.log(c2.colors) // "['red', 'blue', 'green']"

7. class extends

js
class Parent {
  constructor(name) {
    this.name = name
    this.colors = ['red', 'blue', 'green']
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name) // 继承父类属性
    this.age = age
  }
}

//

const c1 = new Child('child1', 10)
c1.colors.push('black')
console.log(c1)
console.log(c1.name) // "child1"
console.log(c1.age) // 10
console.log(c1.colors) // "['red', 'blue', 'green', 'black']"

console.log('---')

const c2 = new Child('child2', 20)
console.log(c2)
console.log(c2.name) // "child2"
console.log(c2.age) // 20
console.log(c2.colors) // "['red', 'blue', 'green']"
参考