1.原型链
每个对象都有一个原型(prototype),并从原型上继承属性和方法。原型本身也是一个对象,它也有自己的原型,形成一个链式结构。这种链式结构就被称为原型链。
prototype 属性
prototype 是函数特有的属性
一句话概括prototype的作用:让某一个构造函数实例化的所有对象可以找到公共的方法和属性。
__proto__ 属性
__proto__ 属性是对象特有的属性。它表示当前对象的原型对象是谁。
var Parent = function(){
}
Parent.prototype.name = "所有Parent的实例都可以读取到我"
let p1 = new Parent()
let p2 = new Parent()
p1.name // “所有Parent的实例都可以读取到我”
p2.name // “所有Parent的实例都可以读取到我”
p1.__proto === Parent.prototype // true 实例对象的隐式原型等于构造函数的显式原型。
graph TD
p1对象有没有name属性 --> p1.__proto__(Parent.prototype)--> Object.prototype上有没有name属性--> null
原型链继承的缺点
原型链继承的一个主要问题是包含引用类型值的原型属性会被所有实例共享。换而言之,如果一个实例改变了该属性,那么其他实例的该属性也会被改变
构造函数继承
构造函数继承,通过使用call或apply方法,我们可以在子类中执行父类型构造函数,从而实现继承。
优点:这种继承方式的好处是,原型属性不会被共享
缺点:它不能继承父类prototype上的属性
//父类
function Parent(){
this.sayHello = function(){
console.log("Hello")
}
}
Parent.prototype.a = "我是父类prototype上的属性"
//子类
function Child(){
Parent.call(this)
}
// 创建两个Child实例
var child1 = new Child()
var child2 = new Child()
var parentObj = new Parent()
console.log(child1.sayHello === child2.sayHello) // 输出false
//它不能继承父类prototype上的属性
console.log(parentObj.a)//"我是父类prototype上的属性"
console.log(child1.a) // undefined
组合继承
优点: 1.原型属性不会被共享 2.可以继承父类的原型链上的属性和方法
缺点: 调用了2次Parent() 它在child的prototype 上添加了父类的属性和方法
//父类
function Parent(){
this.sayHello = function(){
console.log("Hello")
}
}
Parent.prototype.a = "我是父类prototype上的属性"
//子类
function Child(){
//调用第一次Parent
Parent.call(this)
}
Child.prototype = new Parent()//调用第二次Parent的时候,它在Child的prototype 上添加了父类的属性和方法(sayHello),调用第一次Parent的时候已经在Child实例上就有啦。
var child1 = new Child()
console.log(child1.a) //我是父类prototype上的属性
寄生组合继承
优点: 1.原型属性不会被共享 2.可以继承父类的原型链上的属性和方法 3.只调用了1次Parent().因此,它不会在Child的prototype上添加Parent的属性和方法
缺点:Child.prototype 的原始属性和方法会丢失
//父类
function Parent(){
this.sayHello = function(){
console.log("Hello")
}
}
Parent.prototype.a = "我是父类prototype上的属性"
//子类
function Child(){
Parent.call(this)
}
Child.prototype.childFunction = () => {
console.log("我是child方法")
}
// 创建一个没有实例方法的父类实例作为子类的原型
//Object.create()创建一个空对象,空对象的原型Parent.prototype
Child.prototype = Object.create(Parent.prototype)
//修复构造函数的指向
Child.prototype.constructor = Child
// 创建两个Child实例
var child1 = new Child()
console.log(child1.a) //我是父类prototype上的属性
constructor
constructor是对象特有的属性,它表示当前对象的构造函数。我们使用构造函数Parent()创建了实例对象p1.那么p1的constructor 就是 Parent().
var Parent = function(){}
Parent.prototype.name = "所有Parent的实例都可"
let p1 = new Parent()
console.log(p1.constructor) //f Parent(){}
console.log(Parent.constructor) // f Function(){}
//函数其实也是个对象,是由Function函数实例化得到的对象。所以Parent.constructor 是Function
//Function.constructor 是自己本身,所有的构造函数都是由Function所构造出来的。
console.log(Function.constructor) // f Function(){}
实战
Function.prototype.a = () =>{
console.log(1)
}
Object.prototype.b = () =>{
console.log(2)
}
function A(){}
const a = new A()
a.a()
a.b()
A.a()
A.b()
对于new 出来的对象a的属性,查找的顺序如下:
1.a 自身
2.‘a.__proto__’相当于A.prototype
3."A.prototype.__proto__"相当于Object.prototype
4."Object.prototype.__proto__"这个为null,原型链查找到头
对于function 定义的函数A的属性,查找顺序如下:
1.A自身
2.‘A.__proto__’相当于Function.prototype
3.‘Function.prototype.__proto__’相当于Object.prototype
4."Object.prototype.__proto__"这个为null,原型链查找到头
1.对象是通过new 操作符构建出来的,所以对象之间不相等:
2.对象注意:引用类型
3.对象的key 都是字符串类型
4.对象如何找属性|方法
现在对象本身找 ==》构造函数中找 ==〉对象原型中找 ==》构造函数原型中找 ==〉 对象上一层原型查找
考题1 对象是通过new操作符构建出来的,所以对象之间不相等
[1,2,3] === [1,2,3]
考题2 对象注意:引用类型
var obj1 = {
a:'hello'
}
var obj2 = obj1
obj2.a = 'world'
console.log(obj1) // 'world'
(function(){
console.log(a) // undefined
var a = 1
})()
考题3 对象的key 都是 字符串
var a = {}
var b = {
key:'a'
}
var c = {
key:'c'
}
a[b] = '123' // a['object Object'] = '123'
a[c] = '456' // a['object Object'] = '456'
console.log(a[b]) // '456'
function Fun(){
this.a = '在Fun函数中添加的'
}
Fun.prototype.a = '这是Fun原型添加的'
let obj = new Fun()
obj.a = '对象本身'
obj.__proto__.a = '这是对象原型添加的'
console.log(obj.a) // 调用顺序 obj ‘对象本身’ => new Fun '在Fun函数中添加的' => __proto__ '这是对象原型添加的' => 'prototype' '这是Fun原型添加的' => =>