继承与原型链
当谈到继承时,jsvascript只有一种结构: 对象。每个实例对象(object)都有一个私有属性(称之为__proto__,隐式原型)指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(隐式原型__proto__),层层向上,直到一个对象的原型对象为null。根据定义,null没有原型,并作为这个原型链中的最后一个环节。
几乎所有的Javascript中的对象都是位于原型链顶端的Object的实例
构造函数的原理
javascript的构造函数式的面向对象的编程,其实是把每个实例的原型,挂接在构造函数的原型上,同时在生成实例的过程中,运行构造函数,所以通过this关键字就把传入的参数挂在了新生成的实例的属性上
function Person(name,age){
this.name = name
this.age = age
}
Person.prototype.greet=function(){
console.log('hello,this is'+ this.name)
}
const person1 = new Person('lily', 18)
console.log(person1.greet === Person.prototype.greet) // true
console.log(person1.hasOwnProperty('greet')) // false
console.log(Object.getPrototypeOf(person1)===Person.prototype===person1.__proto__) // true
new实际做了什么
如下案例
function Person(name){
this.name = name
}
const person1 = new Person('lily')
原理代码实现
function newOperator(constructor,arguments){
// 创建了一个新的空对象
const obj = {}
// 把新对象的原型绑到构造函数的原型上
Object.setPrototype(obj,constructor.prototype)
// 构造函数被执行,执行过程中this被绑定在新的对象上
constructor.call(this,arguments)
// 返回这个新的对象
return obj
}
深入理解this关键字
对象的属性可能是一个函数,当引擎遇到对象属性是函数的情况,会将函数单独保存在堆中,然后再将函数的地址赋值给对象属性;而 Javascript 是允许在函数体内引用当前环境的其他变量,那么问题来了,函数可以在不同的运行环境执行,所以我们就需要一种机制,能够在函数内获得当前运行环境,由此诞生了 this ,它的设计目的就是指向函数运行时所在的环境。
如何正确判定 this 指向,待补充
深入理解箭头函数
- 函数体内的this对象是定义时所在的对象,而不是使用时所在的对象
- 没有this,不可以当作构造函数,所以call,bind,apply也无效
- 除了this,agruments、super合new.target也不存在,分别指向外层函数的对应变量
- 不可以使用yeild命令,箭头函数不能用作Generator函数
class详解
class是构造函数的语法糖,它的类型就是函数,类本身就指向构造函数
// es5构造函数的实现方式
function Circle(center,radius){
this.center = center
this.radius = radius
}
Circle.prototype.getCircleReference(){
return 2 * Math.Pi * this.radius
}
const circle = new Circle([1,1],10)
// class的实现方式
class Circle{
constructor(cneter,radius){
this.center = center
this.radius = radius
}
getCircleReference(){
return 2 * Math.Pi * this.radius
}
}
const circle2 = new Circle([1,1],10)
console.log(Object.getprototype(circle2) === Circle.prototype) // true
//在类的实例上调用方法,其实就是调用原型上的方法
console.log(circle2.constructor === Circle.prototype.constructor) // true
class能够很轻松的实现类与类之间的继承,如下
class Shape{
constructor(x,y){
this.x = x
this.y = y
}
move(x,y){
this.x += x
this.y += y
}
}
// 用extends实现类之间的继承
class Circle extends Shape {
constructor(x,y,radius){
super(x,y)
}
caculateArea(){
return Math.pi * Math.pow(this.radius, 2)
}
}
const circle = new Circle(0,0,10)
circle.move(1,1)
es5构造函数实现的继承
// 父类
function Shape(x,y){
this.x = x
this.y = y
}
Shape.prototype.move = function(x,y) {
this.x += x
this.y += y
}
// 子类
function Circle(x,y,radius){
// 需要调用父类的构造函数,才能实现属性的继承
Shape.call(this,x,y)
this.radius = radius
}
// 父类原型上方法的继承
Circle.prototype = Object.create(Shape.prototype)
// 修正Circle.prototype.constructor指针
console.log(Circle.prototype.constructor) // Shape
Circle.prototype.constructor = Circle
console.log(Circle.prototype.constructor) // Circle
// 扩展自己的方法
Circle.prototype.caculateArea = function(){
return Math.pi * Math.pow(this.radius, 2)
}
如上所示,类的继承方式相比起来简介明了,大大的方便了我们的开发
static:class的静态方法
类相当于实例的原型,所有在类中定义的方法都会被实例继承,如果在一个方法前加上static关键字,就表示该方法不会被实例继承,而是直接通过类调用,称为“静态方法”
class Shape{
constructor(x,y){
this.x = x
this.y = y
}
static showMethod(){
console.log('shape')
}
}
Shape.showMethod() // shape
const circle = new Shape()
circle.showMethod() // circle.showMethod is not a function
// 父类的静态方法可以被子类继承
class Circle extends Shape {
static showMethod(){
console.log(supershowMethod() + ' circle')
}
}
Circle.showMethod() // shape circle