3.原型/继承/构造/类篇
1.原型链的概念
JavaScript为我们提供了这个方法:
Object.create(proto)
,这个方法生成一个空对象,并将参数proto设置为自己的原型[[Prototype]]
。原型有什么用呢?简单来说,假如我们在一个对象里找某个属性或方法,没找到,那ascriptive引擎就会继续往这个对象的原型里找,再找不到就继续往这个对象原型的原型里找,直到找到或者遇到null,这个过程就是原型链啦。
2 原型prototype的概念
JavaScript
里创建的每个函数都带有prototype这个属性,它指向一个对象(这个对象里包含一个constructor属性指向原来的这个函数)
3.构造函数
构造函数
constructor
,专门用来构造对象的,使用方法就是在构造函数前使用new
指令。
4. new指令到底做了什么
- 生成一个新对象
- 为这个对象设置prototype
- 使用this执行构造函数
- 返回这个对象
// 简化版
function myNew(constructor, args) {
const obj = {}
Object.setPrototypeOf(obj, constructor.prototype)
constructor.apply(obj, args)
return obj
}
// 正式版
function myNew(constructor, args) {
const obj = Object.create(constructor.prototype)
const argsArray = Array.prototype.slice.apply(arguments)
const result = constructor.apply(obj, argsArray.slice(1))
if(typeof result === 'object' && result !== null) {
return result
}
return obj
}
// es6 进阶法
function myNew(constructor, ...args) {
const obj = Object.create(constructor.prototype)
const result = constructor.apply(obj, args)
if(typeof result === 'object' && result !== null) {
return result
}
return obj
}
这里我还是要继续展开讲一下,我举个例子:
const a = [1, 2, 3]
a.toString()
大家想一下,为什么a这个数组会有一个叫toString的方法?
- 首先你这样声明式的创建了一个数组a,其实背后是JavaScript帮你用
new Array(1, 2, 3)
帮你创建的数组a- 这个Array函数其实就是一个构造函数,结合我们前面讲到的各种知识,可以得出数组a的原型proto就是Array.prototype(
a.__proto__ === Array.prototype
)- 既然数组a上没有toString这个方法,JavaScript就去它的原型Array.prototype上找
- 嘿,找到了
- 假如没找到的话,就会去Array.prototype的原型找(
a.__proto__.__proto__ === Object.prototype
)
5. class 类
es5 写法
function User(name, age) {
this.name = name
this.age = age
}
User.prototype.grow = function(years) {
this.age += years
console.log(`${this.name} is now ${this.age}`)
}
User.prototype.sing = function(song) {
console.log(`${this.name} is now singing ${song}`)
}
const zac = new User('zac', 28)
es6 写法
class User {
constructor(name, age) {
this.name = name
this.age = age
}
grow(years) {
this.age += years
console.log(`${this.name} is now ${this.age}`)
}
sing(song) {
console.log(`${this.name} is now singing ${song}`)
}
}
const zac = new User('zac', 28)
class 背后的运行机制
- 首先创建了一个叫做User的函数
- 然后把class的constructor里面的代码原封不动的放到User函数里
- 最后将class的方法,如grow,sing放到User.prototype里
6. extends 继承
class User {
constructor(name, age) {
this.name = name
this.age = age
}
grow(years) {
this.age += years
console.log(`${this.name} is now ${this.age}`)
}
sing(song) {
console.log(`${this.name} is now singing ${song}`)
}
}
class Admin extends User {
constructor(name, age, address) {
super(name, age) //to call a parent constructor
this.address = address
}
grow(years) {
super.grow(years) // to call a parent method
console.log(`he is admin, he lives in ${this.address}`)
}
}
const zac = new User('zac', 28)
1. super 关键字
为什么要调用super?
因为javascript规定了:通过继承(extends)来而的class,必须在constructor里调用
super()
,否则在constructor里调用this
会报错!
两次调用super的意义?
super(...)
是用来调用父类的constructor方法(只能在constructor里这么调用)super.method(...)
是用来调用父类的方法
2. react super的探究
// React内部的代码
class Component {
constructor(props) {
this.props = props;
// ...
}
}
// 我们自己代码
class Checkbox extends React.Component {
constructor(props) {
super();
console.log(this.props); // undefined
//
}
3._prototype 原型继承的案例探究
let user = {
name: "User",
sing() {
console.log(`${this.name} is singing.`)
}
}
let admin = {
__proto__: user,
name: "Admin",
sing() {
this.__proto__.sing.call(this) //(*)
console.log('calling from admin')
}
}
admin.sing(); // Admin is singing. calling from admin
let admin = {
__proto__: user,
name: "Admin",
sing() {
this.__proto__.sing()
console.log('calling from admin')
}
}
admin.sing(); // User is singing. calling from admin
- 假如是
this.__proto__.sing()
,调用者是this.__proto__
,相当于admin.__proto__
, 也就是user对象,所以最后打印出来的是:User is singing. - 假如是
this.__proto__.sing.call(this)
,这时候我们通过call手动将调用者改为admin了,所以最后打印出来是:Admin is singing
.