Samgao0312 / Blog

MIT License
1 stars 1 forks source link

【再学前端】JavaScript 常见 6 种跨域方式 #120

Open Samgao0312 opened 2 years ago

Samgao0312 commented 2 years ago

prototype原型对象可以理解为一个共享空间,保存了一些方法和属性,提供给 prototype.constructor 构造函数实例的对象使用,有效节省内容。

1. 原型链继承

//父类型
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.play = [1, 2, 3];
  this.setName = function () {};
}
Person.prototype.setAge = function () {};

//子类型
function Student(price) {
  this.price = price;
  this.setScore = function () {};
}

Student.prototype = new Person('G-Dragon', 30); // 子类型的原型为父类型的一个实例对象

var s1 = new Student(15000);
var s2 = new Student(14000);
console.log(s1, s2);  // Person { price: 15000, setScore: [Function] } Person { price: 14000, setScore: [Function] }

// 给父类新增属性 方法
Person.prototype.username = '美男子';
Person.prototype.getInfo = function() {
  return {
    username: this.username,
    name: this.name,
    age: this.age
  }
}
console.log(s1.username)  //美男子
console.log(s2.getInfo())  //{ username: '美男子', name: 'G-Dragon', age: 30 }

image


2. 借用构造函数继承

// 父类
function Parent(name, age) {
  this.name = name,
  this.age = age,
  this.setName = function() {}
}
Parent.prototype.setAge = function() {}

// 子类
function Child(name, age, score) {
  Parent.call(this, name, age)
  // this.name = name,
  // this.age = age,
  this.score = score
}
Child.prototype.haha = function() {
  return '哈哈哈....'
};

let c1 = new Child('高龙', 30, 100)
let c2 = new Child('小强🪳', 32, 98)
console.log(c1) 
// Child {
//   name: '高龙',
//   age: 30,
//   play: [ 1, 2, 3 ],
//   setName: [Function],
//   score: 100 }
console.log(c1.setAge) //undefined, 即表示无法继承父类原型上的属性和方法
console.log(c1.haha())  //哈哈哈....

c1.play = [3, 2, 1]
console.log(c2)
// Child {
//   name: '小强🪳',
//   age: 32,
//   play: [ 1, 2, 3 ],
//   setName: [Function],
//   score: 98 }

该继承只是部分的继承,原因是父类原型上的属性 or 方法子类并没有继承。如下图所示: image


3. 组合式继承 (原型链继承 + 构造函数继承)

同时结合原型链继承、构造函数继承就是组合继承了。

// 父类型
function Person(name, age) {
  this.name = name,
  this.age = age,
  this.setAge = function () { }
}
Person.prototype.setAge = function () {
  console.log("111")
  return this.age;
}

// 子类型
function Student(name, age, price) {
  Person.call(this, name, age)
  this.price = price
  this.setScore = function () { }
}
Student.prototype = new Person()  //子类原型 指向父类的一个实例
Student.prototype.constructor = Student  //组合继承也是需要修复构造函数指向的
Student.prototype.sayHello = function () { }  

var s1 = new Student('Tom', 20, 15000)
var s2 = new Student('Jack', 22, 14000)
var p1 = new Person('Sam', 30)
console.log(s1)
console.log(s1.constructor) //Student
console.log(p1.constructor) //Person

4. 原型式继承

5. 寄生式继承

6. 寄生组合继承

function Parent() {
  this.name = 'fedaily'
}

Parent.prototype.getName = function() {
  return this.name
}

function Child() {
  Parent.call(this)
  this.topic = 'fe'
}

// 仔细看这个函数的实现
inherit(Child, Parent)
function inherit(child, parent) {
  var prototype = object(parent.prototype)
  prototype.constructor = child
  child.prototype = prototype
}

// 这个函数的作用可以理解为复制了一份父类的原型对象
// 如果直接将子类的原型对象赋值为父类原型对象
// 那么修改子类原型对象其实就相当于修改了父类的原型对象
function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

7、ES6中Class的继承

ES6中引入了class关键字,class可以通过extends关键字实现继承,还可以通过static关键字定义类的静态方法,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。 ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

需要注意的是,class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的。

class Person {
  //调用类的构造方法
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  //定义一般的方法
  showName() {
    console.log('调用父类的方法');
    console.log(this.name, this.age);
  }
}
let p1 = new Person('kobe', 39);
console.log(p1);

//定义一个子类
class Student extends Person {
  constructor(name, age, salary) {
    super(name, age); //通过super调用父类的构造方法
    this.salary = salary;
  }
  showName() {
    //在子类自身定义方法
    console.log('调用子类的方法');
    console.log(this.name, this.age, this.salary);
  }
}
let s1 = new Student('wade', 38, 1000000000);
console.log(s1);
s1.showName();

参考阅读