高级JS-原型-继承-字面量增强-解构

108 阅读5分钟

原型

原型的作用:

  1. 通过引用对象的属性key来获取一个value时,它会触发 [[Get]]的操作
  2. 如果我在自己的属性里面没找到,就会去原型上面去找
  • 对象的原型
var obj = {
    name: 'www',
    age: 19
}
// 获取obj原型
console.log(obj.__proto__); // 浏览器实现的(非标准的)
console.log(Object.getPrototypeOf(obj)); // (标准的)
  • 函数的原型 prototype
function foo() {}
var obj = {}
// 1. 构造函数看作是一个普通的对象时,他是具备__proto__(隐式原型)
Object.prototype(foo.__proto__)
Object.prototype(obj.__proto__)

// 2. 将函数看作一个函数时,他是具备prototype(显示原型),对象没有这个原型
console.log(foo.prototype);
  • new操作符

当我们多个对象拥有共同的值时,可以将它放到构造函数对象的显示原型

由构造函数创建出来的所有对象,都会共享这些属性

function Student(name) {
    this.name = name
}

Student.prototype.running = function () {
    console.log(this.name + 'running');
}

let stu1 = new Student('wqq', 22)
let stu2 = new Student('qq', 22)

stu1.running()
stu2.running()

当通过new关键字去调用这个构造函数,会做一下操作:

  1. 创建一个空对象
  2. 将this指向这个空对象
  3. 将Foo的prototype(显示原型)赋值给空对象的__proto__(隐式原型)
  4. 执行代码块
  5. 返回这个对象
function Foo() {}

console.log(Foo.prototype);
var fn = new Foo()

console.log(fn.__proto__ === Foo.prototype); // true
  • constructor属性

默认情况下原型上都会添加一个属性叫做constructor,这个constructor指向当前的函数对象

function Person() {}

var personPrototype = Person.prototype // 原型
console.log(personPrototype.constructor === Person); // true
  • 重写函数原型对象
function Person() {}

// 重写原型对象,它没有constructor
Person.prototype = {
    message:'hello',
    name:'wqq',
    constructor:Person // 会做这个操作
}

// 更加精准的做法
Object.defineProperty(Person.prototype,'constructor',{
    enumerable:false,
    configurable:true,
    writable:true,
    value:Person
})
var p1 = new Person()
console.log(p1.message); // hello

继承

{ } 的本质

let obj = {}
// 相当于
let obj2 = new Object()
console.log(obj.__proto__ === Object.prototype);

原型链实现继承

function Person(name,age) {
    this.name = name
    this.age = age
}
Person.prototype.running = function () {
    console.log('Person running');
}

function Student(name, age, height) {
    this.name = name
    this.age = age
    this.height = height
}
Student.prototype.studying = function () {
    console.log('Student studing');
}

方式一:父类的原型直接赋值给子类的原型,Student.prototype = Person.prototype

缺点:共享同一个原型,修改任意一个,另一个也会被修改

Student.prototype = Person.prototype
let p1 = new Person()
p1.studying() // 父类能调用子类的方法

方式二:创建一个父类实例对象 ( new Person() ) ,用这个实例对象作为子类的原型

缺点:不能给Person传递参数

var p = new Person('qqq',19)
Student.prototype = p

let p1 = new Person()
p1.studying()  // 父类不能调用子类的方法

// 创建学生
var stu1 = new Student('www',10,1.66)
stu1.running() // Person running
stu1.studying() // Student studying
console.log(stu1.name, stu1.age); // www 10

借用构造函数继承

在子类型构造函数的内部调用父类型构造函数

/* 缺点:
*  1. 是无论在什么情况下,都会调用两次父类构造函数(创建子类原型、子类实例的时候)
*  2. 父类型中的属性会有两份: 一份在原型对象中, 一份在子类型实例中
* */
function Person(name,age) {
    this.name = name
    this.age = age
}
Person.prototype.running = function () {
    console.log('Person running');
}

function Student(name, age, height) {
    // 借用构造函数,this:Student
    Person.call(this,name,age)
    this.height = height
}

var p = new Person('qqq',19)
Student.prototype = p

Student.prototype.studying = function () {
    console.log('Student studying');
}

// 创建学生
var stu1 = new Student('www',10,1.66)
stu1.running() // Person running
stu1.studying() // Student studying
console.log(stu1.name, stu1.age); // www 10

寄生组合式继承函数

创建一个封装继承过程的函数, 该函数在内部以某种方式来增强对象,最后再将这个对象返回

// 工具函数,创建对象的过程(没有兼容问题)
function createObject(o) {
    function F() {}
    F.prototype = o
    return new F()
}

// 将SubType和SuperType联系到一起(寄生式函数)
function inherit(SubType, SuperType) {
    SubType.prototype = createObject(SuperType.prototype)
    Object.defineProperty(SubType.prototype, 'constructor', {
        enumerable: false,
        configurable: false,
        writable: true,
        value: SubType
    })
}

function Person(name,age) {
    this.name = name
    this.age = age
}
Person.prototype.running = function () {
    console.log('running');
}

function Student(name,age,height,sno) {
    Person.call(this,name,age)
    this.height = height
    this.sno = sno
}

inherit(Student, Person) // Student 继承自 Person
Student.prototype.studying = function () {
    console.log('studying');
}

var  stu1 = new Student('www',19,1.99,100)
stu1.running()

Object类是所有对象的父类

Object.prototype.message = '吃点香菜'

class定义类

class定义的类不能通过普通的函数进行调用

// es6定义类
class Person {
    // 通过new调用Person类时,默认会调用这个类的构造函数constructor
    constructor(name,age) {
        this.name = name
        this.age = age
    }
    // 实例方法,本质上是放在prototype上的
    running(){
        console.log('running');
    }
    eating(){
        console.log(this.name, 'eating');
    }
}

// 创建实例对象
var p1 = new Person('www',20)
console.log(Person.prototype === p1.__proto__); // true
p1.eating() // www eating

访问器方法

// class定义类
class Person {
    constructor(name,age) {
        this._name = name
        this.age = age
    }
    // 访问器的编写方式
    set name(value) {
        console.log('设置name');
        this._name = value
    }
    get name() {
        console.log('获取name');
        return this._name
    }
}
var p1 = new Person('qq',20)

应用场景:

// class定义类
class Rectangle {
    constructor(x, y, w, h) {
        this.x = x
        this.y = y
        this.w = w
        this.h = h
    }
    get position() {
        return {x: this.x, y: this.y}
    }
    get size(){
        return {w:this.w,h:this.h}
    }
}

var p1 = new Rectangle(10, 20, 100, 200)
console.log(p1.position); // {x: 10, y: 20}
console.log(p1.size); // {w: 100, h: 200}

类的静态方法

class Person {
   // 实例方法
   runing() {
       console.log(this); // p
       console.log('running');
   }
   // 类方法(静态方法)
   static eating() {
       console.log(this); // Person
       console.log('eating');
   }
}

var p = new Person()
p.runing() // running
Person.eating() // eating

ES6实现继承

// 定义父类
class Person{
    constructor(name,age) {
        this.name = name
        this.age = age
    }
    running(){
        console.log('running');
    }
}

class Student extends Person{
    constructor(name,age,sno,score) {
        super(name,age)
        this.sno = sno
        this.score = score
    }
}

class Teacher extends Person{
    constructor(name,age,title) {
        super(name,age)
        this.title = title
    }
}

var stu1 = new Student('ww',100)
stu1.running() // running

super关键字

在子(派生)类的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数

class Animal{
    running(){
        console.log('running');
    }
    static sleep(){
        console.log('animal sleep');
    }
}

class Dog extends Animal{
    // 子类对继承过来的方法不满意,可以重写
    running() {
        console.log('dog四条腿running');
        super.running();  // 调用父类的方法
    }

    static sleep(){
        console.log('趴着');
        super.sleep()
    }
}
var dog = new Dog()
dog.running()

Dog.sleep()

类的混入mixin

js只支持单继承(不支持多继承)

function mixinAnimal(BaseClass) {
  return class extends BaseClass{
        running(){
            console.log('running');
        }
    }
}

function mixinRunning(BaseClass) {
  return class extends BaseClass{
      flying(){
          console.log('flying');
      }
  }
}

class Bird {}
// var NewBird = mixinRunning(mixinAnimal(Bird))
class NewBird extends mixinRunning(mixinAnimal(Bird)){}
var bird = new NewBird()
bird.running()
bird.flying()

多态

面向对象的三大特性:封装、继承、多态

不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现

// 继承是多态的前提
function sum(a, b) {
    console.log(a + b);
}
sum(10,20)
sum('abc','efg')

字面量的增强

var name = 'www'
var age = 19
var key = 'address'
var obj = {
    // 1. 属性增强
    name,
    age,
    // 2. 方法增强
    eating:function () {
        console.log(this); // obj
    },
    swimming:()=>{
        console.log(this); // window
    },
    running(){
        console.log(this); // obj
    },
    // 3. 计算属性名的增强
    [key]:'成都'
}
obj.eating()
obj.swimming()
obj.running()

function foo() {
    var message = 'hello'
    var info = 'wqq'
    return {message,info}
}

解构

// 数组的解构,有严格的顺序要求
var arr = ['aa', 'bb', undefined, 'cc', 'dd']

var [a, b] = arr
console.log(a, b); // aa bb

var [a, , b] = arr 
console.log(a, b); // aa cc

var [a, b, ...c] = arr
console.log(a, b, c); // aa bb ['cc','dd']

var [a, b, c = 'default'] = arr
console.log(a, b, c); // aa bb default


// 对象的解构(没有顺序,根据key来解构)
var obj = {name: 'www', age: 19, height: 1.66}

var {name, age, height} = obj
console.log(name, age, height); // www 19 1.66

var {height:h,name} = obj
console.log(h, name); // 1.66 'www'

var {name,age,add='默认值'} = obj
console.log(name, age, add); // www 19 默认值