复习笔记 JavaScript精粹 第5章 继承

154 阅读5分钟

前年看这本书的时候居然没有做第5章的笔记,应该是当时没看懂吧.就算是现在来看,也花了几个小时才完全看懂.现在终于算是知道什么是函数式编程了,现学现用,前天(2019-10-23)就把函数式编程的方式应用到线上代码中去了...确实代码结构看起来要清晰很多,接下来两周把当前维护的这个项目全部改成函数式调用...哇咔咔

  • 继承的作用是什么?

    • 可以大量复用代码,减少开发工作量
    • 引入类型系统的范围,提升程序稳定性
  • JavaScript中的继承和其他面向对象编程语言中的继承是什么区别?

    • 面向对象中的继承,是基于类的,对象是类的实
    • JavaScript中的继承,是基于原型的,对象直接继承其他对象

伪类

  • 什么是伪类
    • 就是模拟面向对象编程语言
    • 以类的形式进行继承

一个继承的例子

// 1. 定义一个方法,变量以大写开头(称之为构造器)
var Mammal = function (name) {
  this.name = name;
}
// 2. 在该构造器的原型上添加2个方法(用于继承)
Mammal.prototype.get_name = function () {
  console.log(this.name);
}
Mammal.prototype.says = function () {
  console.log(this.saying || '');
}
// 3. 构造一个实例,他会继承Manmmal的原型
var myMammal = new Mammal('cat');
// 4. 调用实例上的属性/方法
console.log(myMammal.name) // => cat
myMammal.get_name(); // => cat
myMammal.says(); // => ''

// 如果能搞清楚上面3个方法,为什么会输出这个值,原型应该就能够及格了
  • 一个伪类的例子
// 重复上面的1,2步
// 4. 再定义一个构造器
var Cat = function (name) {
  this.name = '小猫';
  this.saying = '喵~';
}
// 5. 重置构造器的原型(模拟继承)
Cat.prototype = new Mammal();
// 6. 在原型上添加新的方法
Cat.prototype.get_name = function () {
  console.log(this.name);
}
Cat.prototype.purr = function (n) {
  console.log(Array.from(new Array(n), () => '呼~').join(''));
}
// 7. 构造一个实例
var myCat = new Cat('我的猫');
console.log(myCat.name); // => 小猫
myCat.get_name();  // => 小猫
myCat.purr(4); // => 呼~呼~呼~呼~
myCat.says(); // => 喵~
// 如果能弄清楚上面4个方法,分别是执行的原型链上那一环的方法,原型应该就掌握到7分了
  • new的原理
    • 创建函数时,会自动添加一个prototype对象,它包含一个constructor属性,constructor的值为当前函数
    • new这个函数创建一个新对象,新对象会自动添加一个__proto__对象,__proto__的值为函数的prototype
    • 如果函数返回的不是一个对象,则返回新对象

prototype,__proto__的关系

// 1. 定义一个构造器
var Person = function () {
    this.name = '人';
} // => Person.prototype.constructor === Person
// 2. 构造一个新对象
var chinese = new Person(); // => chinese.__proto__ === Person.prototype

new的原理,如果把new看成一个方法,则是下面的代码

// 1. 定义一个方法,用于快速创建继承某个对象的对象
Object.create = function(obj) {
  var Func = function () {};
  Func.prototype = obj;
  return new Func();
}
// 2. 定义一个方法,用于快速在某个对象的原型上添加方法
Function.prototype.method = function (name, func) {
  this[name] = func;
  return this;
}
// 3. 添加一个方法,模拟new的过程
Function.method('new', func) {
  // 创建一个继承当前对象的对象
  var that = Object.create(this.prototype);
  // 执行当前方法
  var other = this.apply(that, arguments);
  // 如果当前方法返回的不是对象,则返回继承的对象
  return (typeof other === 'object' && other) || that;
}

var myPerson = Person.new(); // => 效果等同于new Person();
  • 伪类继承的缺点
    • 所有属性都是公开的
    • 无法访问父类的属性
    • 如果忘记加new,则会创建全局变量

原型

上面是通过new模拟基于类的继承,在JavaScript当中,完全可以直接基于对象来进行继承.

一个基于对象继承的例子

// 1 定义一个对象
var myMammal = {
  name: '小动物',
  get_name: function() {
    console.log(this.name);
  },
  syas: function() {
    console.log(this.saying || '');
  }
};

// 2  创建一个基于该对象的新对象
var myCat = Object.create(myMammal);

// 3 给新对象添加新的属性
myCat.name = '小猫';
myCat.saying = '喵';
myCat.purr = function() {
  console.log('呼~呼~呼')
}
myCat.get_name = function() {
  console.log(`${this.name}${this.saying}`);
}

// 上面定义的新方法,会覆盖原型链上的方法,就叫做差异化继承

函数化

基于原型继承的缺点就是无法隐藏私有变量,可以通过函数化的方式来解决.

  • 什么是函数化呢?

    • 不直接定义对象
    • 而通过一个函数,利用第4章模块的原理,生成一个对象的过程
  • 构造一个生成对象的函数的4个步骤

    • 创建一个新对象
    • 定义私有变量和方法
    • 给这个新对象扩充方法
    • 返回这个新对象

构造一个生成对象的函数步骤

// 定义一个函数化方法
var constructor = function (spec, my) {
  // 1. 创建一个新对象
  var that = {};
  // 2. 创建私有变量
  var a = '';
  var b = function () {};
  // 3. 给新对象添加新的属性
  that.a1 = function () {
    console.log(this.a);
  }
  // 4. 返回这个新对象
  return that;
}
  • 定义一个新对象的4种方法

    • 对象字面量
    • new构造
    • Object.create
    • 生成对象的函数
  • 函数化的优势

    • 相比伪类来说,工作量更少
    • 更好的实现封装和隐藏
    • 可以访问父类方法

一个函数化的例子

// 定义一个函数化函数
var mammal = function(spec) {
  var that = {};
  that.get_name = function () {
    console.log(spec.name);
  }
  that.syas = function () {
    console.log(spec.saying);
  }
  return that;
}

// 函数化继承
var cat = function (spec)) {
  // 2个私有变量,外部无法访问
  spec.saying = spec.saying || '喵';
  spec.name = '小猫';
  // 构建一个新对象,(优势1的体现)
  var that = mammal();
  // 添加新方法
  that.purr = function() {
   console.log('呼~呼~呼');
  }
  // 添加特权方法,用于访问私有变量(优势2的体现)
  that.get_name = function () {
    console.log(`${that.says()}${spec.name}`)
  }
  // 添加一个方法用于访问父类方法(优势3的体现)
  // 这里还没有完全搞明白....太绕了
  that.get_parent_name = function () {
    return '';
  // 返回新对象
  }
  return that;
}

部件

部件的作用也没太看懂..以后再回来看吧