zoukankan      html  css  js  c++  java
  • 编写高质量JS代码的68个有效方法(八)

    [20141227]编写高质量JS代码的68个有效方法(八)

    NO.36、只将实例状态存储在实例对象中

    Tips:

    1. 共享可变数据可能会出问题,因为原型是被其所有的实例共享的
    2. 将可变的实例存储在实例对象中

    一般来说,由于原型属性指向的对象是所有实例共享的。所以不建议在原型指向的对象中存储共享数据。下面给一个简单的例子:

    var Person = function(name){
        this.name = name;
    };
    Person.prototype = {
        children: [],
        addChild: function(childName){
            this.children.push(childName);
        },
        getChildren: function(){
            return this.children;
        }
    };
    
    var p1 = new Person('P1');
    var p2 = new Person('P2');
    p2.addChild('P2_C1');
    console.log(p1.getChildren());
    

    结果比较明显。p2的孩子成p1的了。标准做法是将children存储在实例对象中。

    var Person = function(name){
        this.name = name;
        this.children = [];
    };
    Person.prototype = {
        addChild: function(childName){
            this.children.push(childName);
        },
        getChildren: function(){
            return this.children;
        }
    };
    

    No.37、认识到this变量的隐式绑定问题

    Tips:

    1. this变量的作用域总是有其最近的封闭函数所确定
    2. 使用一个局部变量(通常命名为self,me,that)使得this的绑定对于内部函数是可用的。

    老规矩,看一个简单的示例:

    var testObj = {
        a1: 0,
        fun1: function(){
            function fun2(){
                console.log(this.a1);
            }
            fun2();
        }
    };
    testObj.fun1();
    

    为什么会这样呢?因为this变量是以不同的方式被绑定的。每个函数都有一个this变量的隐式绑定。this变量是隐式的绑定到最近的封闭函数。针对以上的问题,可以有集中方法来处理,参考如下:

    //通过将this用变量self保存的方式实现
    var testObj = {
        a1: 0,
        fun1: function(){
            var self = this;
            function fun2(){
                console.log(self.a1);
            }
            fun2();
        }
    };
    testObj.fun1();
    
    //通过call方法指定接收者(也可以用apply)
    var testObj = {
        a1: 0,
        fun1: function(){
            function fun2(){
                console.log(this.a1);
            }
            fun2.call(this);
        }
    };
    testObj.fun1();
    
    //通过bind来实现
    var testObj = {
        a1: 1,
        fun1: function(){
            function fun2(){
                console.log(this.a1);
            }
            fun2.bind(this)();
        }
    };
    testObj.fun1();
    

    No.38、在子类的构造函数中调用父类的构造函数

    Tips:

    1. 在子类构造函数中显式地传入this作为显式的接收者调用父类的构造函数
    2. 使用Object.create函数来构造子类的原型对象以避免调用父类的构造

    JS中实现的继承:

    var Animal = function(){
        this.weight = 50;
    };
    Animal.prototype.eat = function(){
        console.log('eat food...');
    };
    
    var Dog = function(){
        Animal.call(this);
        Dog.prototype = Object.create(Animal.prototype);
    };
    
    var dog = new Dog();
    console.log(dog.weight);
    

    No.39、不要重用父类的属性名

    Tips:

    1. 留意父类使用的所有属性名
    2. 不要再子类中重用父类的属性名

    由于JS中,属性都是key-value存储,那么同名的属性指向同样的地址,所以以下代码:

    var Animal = function(){
        this.weight = 50;
        this.id = ++Animal.nextId;
    };
    Animal.nextId = 0;
    Animal.prototype.eat = function(){
        console.log('eat food...');
    };
    
    var Dog = function(){
        Animal.call(this);
        this.id = ++ Dog.nextId;
        Dog.prototype = Object.create(Animal.prototype);
    };
    Dog.nextId = 0;
    
    var dog = new Dog();
    console.log(dog.id);
    

    两个类都试图给实例属性id写数据。

    No.40、避免继承标准类

    Tips:

    1. 继承标准类往往会由于一些特殊的内部属性(如[[Class]])而被破坏
    2. 使用属性委托优于继承标准类

    扩展标注库使得其功能更强大是很有诱惑力的,但不幸的是它们的定义具有很多特殊的行为,所以很难写出正确的子类。

    var ArrayEx = function(){
        for(var i = 0, len = arguments.length; i<len ; i++){
            this[i] = arguments[i];
        }
    };
    ArrayEx.prototype = Object.create(Array.prototype);
    
    var ar = new ArrayEx('1', '2');
    console.log(ar.length) //猜猜结果是什么?
    

    原因分析:length属性只对在内部标记为“真正的”数组对象才起作用。直接继承的对象并没有继承 Array的标记标签属性[[Class]]。测试如下:

    var ar = new ArrayEx('1', '2');
    console.log(Object.prototype.toString.call(ar)); //[object Object]
    console.log(Object.prototype.toString.call([])); //[object Array]
    

    ECMAScript标准库中干掉大多数构造函数都有类似的问题。基于这个原因,最好避免继承一下的标准类: Array,Boolean,Date,Function,Number,RegExp或String。

    要想实现类似的功能,可以采用属性委托的方式:

    var ArrayEx = function(){
        this.array = []
        for(var i = 0, len = arguments.length; i<len ; i++){
            this.array[i] = arguments[i];
        }
    };
    ArrayEx.prototype.forEach = function(f, thisArg){
        if(typeof thisArg === 'undefined'){
            thisArg = this;
        }
        this.array.forEach(f, thisArg);
    };
    
    var ar = new ArrayEx('1sfdfsd', '2fdsfs');
    ar.forEach(function(item, i){
        console.log(item);
    });
    
  • 相关阅读:
    据说很少有看到第13个不笑的
    亲个嘴竟然有这么大的学问
    让人心疼的12句话,哪句说到你的痛了
    IDEAd代码注释插件使用javadoc
    连接linux里面的数据库
    代码版本管理工具,定要先更新后提交,git提交本地,推送远程
    visual studio 新特性展望假新闻
    (转)一个美国老工程师的心理话: 给年轻工程师的十大忠告
    随想一
    HongYing HtmlViewer
  • 原文地址:https://www.cnblogs.com/humin/p/4188259.html
Copyright © 2011-2022 走看看