zhangdizhangdi

原型、原型链、继承

原型、原型链

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

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

参考

构造函数

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

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

方法

new

  1. 创建一个空的简单对象
  2. 新对象的 [[Prototype]] 指向构造函数的原型对象(prototype)
  3. 新对象作为this的上下文
  4. 如果函数没有返回其他对象,那么返回这个新对象
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 _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
}

function Test() {
  console.log('Test function')
}
const t = new Test()

console.log('Test', _instanceof(t, Test)) // true
console.log('Function', _instanceof(t, Function)) // false
console.log('Object', _instanceof(t, Object)) // 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('c.__proto__', c.__proto__) // {n: 2, m: 3}
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
js
function Fn() {
  var n = 10
  this.m = 20
  this.aa = function () {
    console.log(this.m)
  }
}

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

var f1 = new Fn()

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

var f2 = new Fn()

console.log(f1.constructor) // ==> function Fn(){...}
console.log(f2.constructor) // ==> Object() { [native code] }
f1.bb() // undefined
f1.aa() // 20
f2.aa() // 20
f2.__proto__.aa() // NaN
f2.bb() //  Uncaught TypeError: 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)
  }
  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

new Foo.getName() // 2
// new (Foo.getName)(); // 2
new Foo().getName() // 3
// (new Foo()).getName(); // 3
new new Foo().getName() // 3
js
function Foo() {
  this.getName = function () {
    console.log(1)
  }
  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

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

继承

实现方法:
  1. 原型链继承
  2. 借用构造函数
  3. 组合继承
  4. 原型式继承
  5. 寄生式继承
  6. 寄生组合式继承
  7. ES6 Class 继承 extends
  8. 混入式继承多个对象
参考