原型、原型链、继承
原型、原型链
每个对象拥有一个原型对象。
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
参考
- 继承与原型链 - MDN
- 对象原型 - MDN
- Object - MDN
- Object.prototype.constructor - MDN
Object.prototype.proto- MDN
- class constructor - MDN
- js 从原型链到继承——图解来龙去脉 - 掘金
构造函数
构造函数本身就是一个函数,与普通函数没有任何区别,不过为了规范一般将其首字母大写。
构造函数和普通函数的区别在于,使用 new 生成实例的函数就是构造函数,直接调用的就是普通函数。
Object 实例的 __proto__ 访问器属性暴露了此对象的 [[Prototype]](一个对象或 null)。
原型
每个函数都有一个 prototype 属性,指向调用该构造函数而创建的实例的原型。每个对象都有一个隐式属性 __proto__(或 [[Prototype]]),指向它的原型对象。
- 实例 通过
__proto__指向原型 - 构造函数 通过
prototype指向原型 - 原型 通过
constructor指回构造函数

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

方法
- Object.create() - MDN
- Object.getPrototypeOf() - MDN
- Object.setPrototypeOf() - MDN
- new - MDN
- new.target - MDN
- instanceof - MDN
- Object.prototype.isPrototypeOf() - MDN
new
- 创建一个空的简单对象
- 新对象的 [[Prototype]] 指向构造函数的原型对象(prototype)
- 新对象作为 this 的上下文
- 执行构造函数内部的代码
- 如果函数没有返回其他对象,那么返回这个新对象
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
继承
实现方法:
- 原型链继承
- 借用构造函数
- 组合继承
- 原型式继承
- 寄生式继承
- 寄生组合式继承
- 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']"
参考
- JavaScript 常用八种继承方案 - 木易杨 掘金
- JavaScript 深入之继承的多种方式和优缺点 - 冴羽 GitHub