目錄:
  1. 继承(Inheritance)
    1. 伪类(Pseudoclassical)
      1. 对象说明符(Object Specifiers)
        1. 原型(Prototypal)
          1. 函数化(Functional)

            JavaScript语言精粹-笔记3-继承

            閱讀時間:全文 1353 字,預估用時 7 分鐘
            創作日期:2020-04-26
            文章標籤:
             
            BEGIN

            继承(Inheritance)

            在基于类的语言中, 对象是类的实例, 并且类可以从另一个类继承. JavaScript是一门基于原型的语言, 这意味着对象直接从其他对象继承.

            伪类(Pseudoclassical)

            文章没有对伪类做解释, 本人理解是JavaScript是没有类这个概念的, 但却实现了一套和类很相似的机制, 由于它本身并不是类的实现, 因此我们这里把这种实现称为伪类.

            当一个函数对象被创建时, Function构造器产生的函数对象会运行类似这段代码: this.prototype = { constructor: this }, 新函数对象被赋予一个prototype属性, 它的值是一个包含constructor属性且属性值为该函数的对象. 这个prototype对象是存放继承特征的地方. 因为JavaScript语言没有提供一种方法去确定哪个函数是打算用来做构造器的, 所以每个函数都会得到一个prototype对象. constructor属性没有什么用, 重要的是prototype对象.

            说白了就是, this.prototype是什么就是继承什么, 要实现继承关系只要将继承对象赋值给this.prototype就可以完成继承

            上面这段话需要仔细得品读, 我们做个试验:

            // 创建函数字面量func
            var Func = function() {};
            // 验证上面的结论
            Func.prototype.constructor === Func // true
            
            // 实例化func赋值给funcer
            var funcer = new func();
            funcer.constructor === Func // true
            
            // 可见 Func.prototype.constructor === funcer.constructor === Func
            // 我们好好想想到底new做了什么

            当使用构造器调用模式, new做了什么, 我们可以将new操作符转换成new方法看下:

            // Function.method方法是前面文章定义好的
            Function.method('new', function () {
              // 创建一个新对象, 它继承自构造器函数的原型对象, 这一句就相当与new操作符了
              var that = Object.create(this.prototype);
              // 调用构造器函数, 绑定this到新对象上, 这一句用于捆绑参数
              var other = this.apply(that, arguments);
              // 如果它的返回值不是一个对象, 就返回该对象
              return (typeof other === 'object' && other) || that;
            });

            接下来看看继承到底做了什么?

            新建一个叫Mammal的伪类

            // 定义一个伪类并扩充它的原型:
            var Mammal = function (name) {
              this.name = name;
            };
            Mammal.prototype.get_name = function () {
              return this.name;
            }
            Mammal.prototype.says = function () {
              return this.saying || '';
            }
            
            // 创造一个实例
            var myMammal = new Mammal('Herb the Mammal');
            var name = myMammal.get_name(); // Herb the Mammal

            新建一个叫Cat的伪类并继承自Mammal

            // 创建另一个伪类Cat
            var Cat = function (name) {
              this.name = name;
              this.saying = 'meow';
            }
            
            // 实现Cat继承于Mammal
            Cat.prototype = new Mammal();
            
            // 扩充原型对象, 增加purr和get_name方法
            Cat.prototype.purr = function (n) {
              var i, s = '';
              for (i = 0; i < n; i ++) {
                if (s) {
                  s += '-';
                }
                s += 'r';
              }
              return s;
            };
            Cat.prototype.get_name = function () {
              return this.say() + ' ' + this.name + ' ' + this.says();
            }
            
            var myCat = new Cat('Henrietta');
            var says = myCat.say(); // meow
            var purr = myCat.purr(5); // r-r-r-r-r
            var name = myCat.get_name(); // meow Henrietta meow

            上面的Cat通过修改prototype为new Mammal()来继承Mammal伪类, 并可以看到新的get_name函数替代了Mammal的get_name函数, 且say函数可以继续使用, 不过代码但看起来有些格格不入, 我们做一些修改:

            // 在Function上定义继承函数inherits
            Function.method('inherits', function (Parent) {
              this.prototype = new Parent();
              return this;
            });
            
            // 通过之前定义的method和inherits函数对代码作变更
            var Cat = function (name) {
              this.name = name;
              this.saying = 'meow';
            }
            .inherits(Mammal)
            .method('purr', function (n) {
              var i, s = '';
              for (i = 0; i < n; i ++) {
                if (s) {
                  s += '-';
                }
                s += 'r';
              }
              return s;
            })
            .method('get_name', function () {
              return this.say() + ' ' + this.name + ' ' + this.says();
            })

            对象说明符(Object Specifiers)

            其实就是让我们将传参方式从一个一个传入变为传入一个对象

            原型(Prototypal)

            在一个纯粹的原型模式中, 我们会摒弃类, 转而专注于对象. 基于原型的继承相比基于类的继承在概念上更容易理解. 我们用基于原型的继承来实现和前面基于伪类的继承一样的功能:

            // 定义对象字面量
            var myMammal = {
              name: 'Herb the Mammal',
              get_name: function () {
                return this.name;
              },
              says: function () {
                return this.saying || '';
              },
            };
            // 使用之前定义的Object.create实现继承, 即: myCat.prototype = myMammal
            var myCat = Object.create(myMammal);
            myCat.anme = 'Henrietta';
            myCat.saying = 'meow';
            myCat.purr = function (n) {
              var i, s = '';
              for (i = 0; i < n; i ++) {
                if (s) {
                  s += '-';
                }
                s += 'r';
              }
              return s;
            }
            myCat.get_name = function () {
              return this.say() + ' ' + this.name + ' ' + this.says();
            }

            函数化(Functional)

            不管上面的伪类继承还是原型继承都有一个弱点就是无法保护隐私, 对象的所有属性都是可见的, 我们可以通过结合闭包概念对上面的示例进行优化:

            // 定义对象生成函数mammal
            var mammal = function (spec) {
              var that = {};
              that.get_name = function () {
                return spec.name;
              };
              that.says = function () {
                return spec.saying || '';
              };
              return that;
            }
            
            // 定义对象生成函数cat, 并且继承mammal
            var cat = function (spec) {
              spec.saying = spec.saying || 'meow';
              var that = mammal(spec);
              that.purr = function (n) {
                var i, s = '';
                for (i = 0; i < n; i ++) {
                  if (s) {
                    s += '-';
                  }
                  s += 'r';
                }
                return s;
              };
              that.get_name = function() {
                return that.says() + ' ' + spec.name + ' ' + that.says();
              };
              return that;
            }
            FINISH

            隨機文章
            人生倒計時
            default