JavaScript中的继承,构造函数以及new关键字的作用


发布者 kris  发布时间 1407809075995
关键字 大话编程  JavaScript 
通常一个构造函数是这样子的,它带有一个area的原型方法,在构造函数中传入长、宽并计算面积。

var Shape = function(width, height) {
    this.width = width;
    this.height = height;
};
Shape.prototype.area = function() {
    return this.width * this.height
};
var shape = new Shape(20, 30);
shape.area();
> 600

输出很完美,这也是经典JavaScript实现继承的方法,通过prototype添加对另外一个对象的引用。

不过一部分JavaScript程序是非常讨厌new关键字的,这个关键字有太浓烈的Java烙印了,而且掩盖了JavaScript基于原型的本质,降低了程序员使用JS语言的效率,因此你想把它省了,看看结果?

注* "JavaScript The Good Parts“的作者Douglas Crockford 曾写道  "A much better alternative is to not use new at all." (page 49),    参见另一程序员对AngularJS的吐嘈, 你已经毁了JavaScript(吐嘈之一就是里面到处都是new)

var shape = Shape(20, 30);
shape.area();
> TypeError: Cannot read property 'area' of undefined

道理再简单不过了,你只不过是执行了一个函数,但是这个函数并没有返回值,所以shape必然是undefined,看来new关键作用之一跟Java是一样的,就是实例化此对象并返回这个对象本身。所以你又试改写了一下函数:

var Shape = function(width, height) {
    var self = this;
    self.area = function() {
        return width * height;
    }
    return self;
};
var shape = Shape(20, 30);
shape.area();
> 600

不错,It works!也不用实例化Shape了,看上去一切正常,代码看上去更简单了,隐藏了不必要的属性,不过似乎你没有意识到你设计了一个潜在的更大的坑,尝试在console中打印这些:

area
> function () { return self.width * self.height; }

为什么are变成了全局变量?你不死心,又在window里打印
window.area
> function () { return width * height; }

结果还一样,其实这一现象在"Javascript: The Good Parts"一书中有很好的解析:

If you forget to include the new prefix when calling a constructor function, then this will not be bound to the new object. Sadly, this will be bound to the global object, so instead of augmenting your new object, you will be clobbering global variables. That is really bad. There is no compile warning, and there is no runtime warning. (page 49)

如果你在一个构造函数中忘了加new,那么这个新对象就不会绑定到新的实例上,不幸的是,它会绑定到全局对象上,你就会影响全局变量。这是非常糟糕的。没有编译警告,并且没有运行时的警告。(第49页)

看来在function函数内使用this绑定公开属性是非常危险的,因为你不确定会不会有人忘写了一个new,或者故意不加一个new,也许这就是为什么基于function的构造函数现在用得越来越少的原因吧,其实解决的办法非常简单,只需更改一处。

var Shape = function(width, height) {
    var self = {};
    self.area = function() {
        return width * height;
    }
    return self;
};

var shape = Shape(20, 30);
shape.area(); >600

其实还有很多其它的解决方案,还有一些开源项目甚至根本不使用构造函数,在此不再累述。





回复 (14)
  • #
  • #1 iSayme 1407821393779
    function Shape(width, height) {
    
      if (!(this instanceof Shape)) {
        return new Shape(width, height);
      }
    
      this.width = width;
      this.height = height;
    
      return this;
    }
    
    Shape.prototype.area = function() {
      return this.width * this.height
    };
    
    
    var shape = Shape(20, 30);
    
    console.log(shape.area());
    
  • #2 kris 1407821880268

    @iSayme #0

    呵,这种解决方案比较正统

  • #3 lingering_river 1407855250956
  • #4 glen 1408280521990

    单例以后,如何实现继承?个人觉得new挺好,有效的隔绝了作用域。

  • #5 kris 1408327175484
    @glen #3

    这个得看个人喜好,基于Object就基本上需要使用Object.create来继承,不过需要单独写一个方法来进行构造函数的工作,如:

    var Shape = function() {
    
        var width  = 0
          , height = 0;
    
        //构造函数
        var init = function(w, h) {
            width  = w;
            height = h;
        }
    
        var area = function() {
            return width * height;
        }
    
        return { init: init, area: area };
    };
    
    //实例化
    var shape = Shape();
    shape.init(20, 30)
    shape.area();
    
    //继承
    var square = Object.create(Shape());
    square.init(30, 30);
    square.area();
    

    基于Function与基于Object皆可,不过个人最近比较倾向于基于Object的。而且构造函数被提取出来以后,一旦构造规则发生改变,再写一个构造函数即可,不会对原有功能造成影响。

  • #6 魏寸为 1437391041508

    f

  • #7 魏寸为 1437391073968

    可以用原型继承,var a = Object.create(obj);

  • #8 nk 1462928959292
    var shape  ={};
    shape  = function(w,h){ return w* h;}
    

    这里提出一个简单的方法,但是稍微复杂或者要带参建议使用new关键字构造方法,如果单单是工具,那么直接封装到一个全局变量就行了。

  • #9 nk 1462929078585
    shape.area = function(w,h){ return w* h;}   //上面掉参
    

    不可修改,不可删除的评论是不是不科学?

  • #10 赵才交 1489143233763

    范文芳 范德萨都是第三方发到付

  • #11 任厌无 1489415976843

    1

  • #12 孙妈丹 1502788858814

    ..

  • #13 孔军问 1522724721348

    www.aijquery.cn 爱jquery 很好的网站

  • #14 蔡共价 1524033069830

    1. 标题111在这里1111111111111111111输入代码111111111111111*强调11111111111111111**加粗文本**文本*1

      #

微信扫码 立即评论