Better

Ethan的博客,欢迎访问交流

JavaScript类数组、对象创建与继承

考虑到内容太多问题,因此准备在学习深入理解过程中,分多次总结,本文续JavaScript深入理解(二)

类数组对象与Arguments

类数组对象

  • 定义:拥有一个 length 属性和若干索引属性的对象
  • 区别:读写,长度,遍历都和数组一样,不同的是数组的API不能直接使用
  • 更多:字符串其实也是类数组对象,可以通过下标访问,但是不能通过下标修改某个字符,仅可读。
    • 大学刚学js的时候,就觉得字符串这个东西很奇怪,我竟然可以直接使用下标访问的方式访问某一个字符,纯数字确不可以,那时候没有类数组的概念,就勉强安慰自己字符串底层是数组的存储方式,实在有点逗哈。
  • 使用数组方法
    • 使用Function.call || Function.apply。
  • 转换成数组
    1. splice
      Array.prototype.splice.call(arrayLike, 0);
      
    2. slice
      Array.prototype.slice.call(arrayLike);
      
    3. ES6 Array.from
      Array.from(arrayLike);
      
    4. concat
      Array.prototype.concat.apply([], arrayLike)
      
    5. ES6 ...运算符
      function func(...arguments) {
      console.log(arguments); // [1, 2, 3]
      }
      
  • Arguments 对象就是一个类数组对象。在客户端 JavaScript 中,一些 DOM 方法(document.getElementsByTagName()等)也返回类数组对象。

Arguments

  • 定义:Arguments 对象只定义在函数体中,包括了函数的参数和其他属性。在函数体中,arguments 指代该函数的 Arguments 对象。
  • length属性:实参的长度
  • callee属性:通过它可以调用函数自身。但是严格模式下,此方法会报错。
    for (var i = 0; i < 3; i++) {
      (data[i] = function () {
         console.log(arguments.callee.i) 
      }).i = i;
    }
    
    还有这种操作!!!因为函数也是一种对象,可以函数添加自定义的属性。
  • arguments 和对应参数是否共享?
    • 传入的参数,实参和 arguments 的值会共享,当没有传入时,实参与 arguments 值不会共享
    • 以上是在非严格模式下,如果是在严格模式下,实参和 arguments 是不会共享的。
  • 应用
    • 参数不定长
    • 函数柯里化
    • 递归调用
    • 函数重载

创建对象的多种方式与优缺点分析

  • 来源:对象创建
  • 对象创建有多种方式,不能说谁优谁劣,关键是掌握分析优缺点的技巧,主要从以下一个方面考虑
    • 是否导致对象无法识别,创建的实例使用 instanceof 能否指向构造函数,无法细分
    • 对象创建原则属性私有,方法共享,方法是否重复创建
    • 封装性好不好
    • 理解字面量重写原型
      • Person.prototype.attr=object扩充原型,不会导致原型丢失属性
      • Person.prototype = {}字面量重写原型,注意是否会导致缺失原本有的属性,比如constructor
  • 组合模式分析
    function Person(name) {
      this.name = name;
    }
    Person.prototype = {
      constructor: Person,
      getName: function () {
          console.log(this.name);
      }
    };
    var person1 = new Person();
    
    分析:
    • 优点:该共享的共享,该私有的私有,使用最广泛的方式
    • 有的人就是希望全部都写在一起,即更好的封装性
  • 使用动态原型模式时,不能用对象字面量重写原型,点击来源,看大神分析

继承的多种方式与优缺点分析

OO语言都支持两种继承方式: 接口继承实现继承 .接口继承只继承方法签名,而实现继承则继承实际的方法.由于js中方法没有签名,在ECMAScript中无法实现接口继承.ECMAScript只支持实现继承,而且其 实现继承 主要是依靠原型链来实现的.

原型和实例

使用原型链后, 我们怎么去判断原型和实例的这种继承关系呢? 方法一般有两种.

  • 第一种是使用 instanceof 操作符, 只要用这个操作符来测试实例(instance)与原型链中出现过的构造函数,结果就会返回true.
    alert(instance instanceof Object);//true
    alert(instance instanceof Father);//true
    alert(instance instanceof Son);//true
    
  • 第二种是使用 isPrototypeOf() 方法, 同样只要是原型链中出现过的原型,isPrototypeOf() 方法就会返回true。
    alert(Object.prototype.isPrototypeOf(instance));//true
    alert(Father.prototype.isPrototypeOf(instance));//true
    alert(Son.prototype.isPrototypeOf(instance));//true
    

属性查找

  • hasOwnProperty
    使用了原型链后,当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,直到查找到达原型链的顶部 ,也就是 Object.prototype ,但是仍然没有找到指定的属性,就会返回 undefined。此时若想避免原型链查找,建议使用 hasOwnProperty 方法.。因为hasOwnProperty 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数
    console.log(instance1.hasOwnProperty('age'));//true
    

多种方式

  • 原型链继承
    Child.prototype = new Parent();
    var child1 = new Child();
    
    • 引用类型的属性被所有实例共享
    • 在创建 Child 的实例时,不能向Parent传参
  • 借用构造函数(经典继承)

    function Child () {
      Parent.call(this);
    }
    var child1 = new Child();
    
    • 避免了引用类型的属性被所有实例共享
    • 可以在 Child 中向 Parent 传参
    • 因为不存在原型链继承,方法必须都在构造函数中定义才能继承,因此每次创建实例都会创建一遍方法。
  • 组合继承

    function Parent (name) {
      this.name = name;
      this.colors = ['red', 'blue', 'green'];
    }
    Parent.prototype.getName = function () {
      console.log(this.name)
    }
    function Child (name, age) {
      Parent.call(this, name);
      this.age = age;
    }
    Child.prototype = new Parent();
    var child1 = new Child('kevin', '18');
    
    • 优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式
    • 缺点是会调用两次父构造函数。
  • 原型式继承
    ES5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型。
    function createObj(o) {
      function F(){}
      F.prototype = o;
      return new F();
    }
    
    • 包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。
    • 那么原型链继承和原型式继承的区别是什么咯?
      原型式继承比原型链多了一层中转,是不是很熟悉?这一点在模拟bind的时候有提到过。
  • 寄生式继承 创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
    function createObj (o) {
      var clone = Object.create(o);
      clone.sayName = function () {
          console.log('hi');
      }
      return clone;
    }
    
    • 缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。
  • 寄生组合式

    • 组合继承最大的缺点是会调用两次父构造函数。那么不使用 Child.prototype = new Parent() ,而是间接的让 Child.prototype 访问到 Parent.prototype。便可以减少父构造函数使用。在组合模式上修改如下:
      var F = function () {};
      F.prototype = Parent.prototype;
      Child.prototype = new F();
      var child1 = new Child('kevin', '18');
      
    • 封装如下:
      function object(o) {
      function F() {}
      F.prototype = o;
      return new F();
      }
      function prototype(child, parent) {
      var prototype = object(parent.prototype);
      prototype.constructor = child;
      child.prototype = prototype;
      }
      // 当我们使用的时候:
      prototype(Child, Parent);
      
    • 《JavaScript高级程序设计》中对寄生组合式继承的夸赞

      这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

  • 几种原型继承理解

      function Parent(){
          this.name='lxq';
      }
      Parent.prototype.sex='f';
      function Child (age) {
        this.age = age;
      }
    
      // way 1
      //Child.prototype=new Parent();
      //var c=new Child(20);
          // 能访问自身属性,父对象属性,父对象原型链上的属性
      //console.log(c.age,c.name,c.sex);//20 "lxq" "f"
    
      //way 2
      //Child.prototype=Parent.prototype;
      //var c=new Child(20);
          // 父对象的属性不能访问到,因为直接原型链直接指向了父的prototype
      //console.log(c.age,c.name,c.sex);//20 undefined "f"
      //Child.prototype.value=1;
          // 会污染父prototype,子原型链上增加属性会影响到父元素原型链
      //console.log(Parent.prototype.value);//1
    
      //way 3 空对象中转,打破子原型链对父元素原型链的印象
      function F(){}
      F.prototype=Parent.prototype;
      Child.prototype=new F();
      var c=new Child(20);
      console.log(c.age,c.name,c.sex);//20 undefined "f"
      Child.prototype.value=1;
      console.log(Parent.prototype.value);//undefined
    


留言

nothing
2017-06-30 14:46

厉害了我的🐷

刘新琼
2017-06-30 14:57

厉害了,你竟然来啦