如何写一个作用域安全的构造函数

更新日期: 2018-11-30阅读量: 1350标签: 函数

基础部分

构造函数本质上就是一个使用new操作符调用的函数,使用new调用时,构造函数内用到的this对象会指向新创建的对象实例:

function Girlfriend(name, age, height) {
  this.name = name;
  this.age = age;
  this.height = height;
}

// 使用new操作符来分配这些属性
var girlfriend = new Girlfriend("Ying", 23, 170);

平时写变量时,如果因为失误忘记使用new操作符时,会造成一个糟糕的影响————因为this对象是在运行时绑定的,直接调用构造函数,this会映射到全局对象window上,导致错误对象属性的增加,增添不必要的变量到全局对象中:

var girlfriend = new Girlfriend("Ying", 23, 170);
console.log(window.name); // "Ying"
console.log(window.age); // 23
console.log(window.height); // 170

特别的,当你自己构造函数内的某些变量名与window变量名重名时(像一些常用变量名name、length等),对这些属性的偶然覆盖就很可能导致其他地方出错,并且这个bug还相当难找!  

在这种情况下构造一个作用域安全的构造函数就显得很有必要:

function Girlfriend(name, age, height) {
  // 首先确认this对象是正确类型的实例,
  // 如果不是就创建新的实例并返回
  if (this instanceof Girlfriend) { // 添加一个检查
    console.log('created');
    this.name = name;
    this.age = age;
    this.height = height;
  } else {
    console.log('new');
    return new Girfriend(name, age, height);
  }
}

var girlfriend1 = Girlfriend("Ying", 23, 170); // "new" "created"
console.log(window.name); // ""
console.log(girfriend1.name); // "Ying"

var girlfriend2 = new Girlfriend("Lin", 22, 165); // "created"
console.log(girfriend1.name); // "Lin"

girlfriend1背后构造函数先new了一个实例并返回实例(打印“new”),再对实例进行赋值(打印“created”)。
girlfriend2自己就先new了一个实例,直接对该实例进行赋值(只打印“created”)。

这样在任何情况下就都可以返回一个安全作用域的实例了。


进阶部分

使用上面添加一个检查的方法可以创建一个作用域安全的构造函数,但如果有的函数窃取该函数的继承且没有使用原型链,那这个继承将被破坏不生效:

function Bmi(sex, weight=1, height=1) { // ES6开始支持的默认值
  if (this instanceof Bmi) {
    this.sex = sex;
    this.weight = weight;
    this.height = height;
    this.getBmi = function() {
      return this.weight / (this.height ** 2);
    };
  } else {
    return new Bmi(sex);
  }
}

function People(height, weight) {
  Bmi.call(this, 'male');
  this.height = height;
  this.weight = weight;
}

var guy = new People(1.75, 68); // 单位是m和kg
console.log(guy.sex) // undefined

Bmi构造函数作用域是安全的,但People并不是。新创建一个People实例后,这个实例准备通过Bmi.call()来继承Bmi的sex属性,但由于Bmi的作用域是安全的,this对象并非是Bmi的实例,所以Bmi会先自己创建一个新的Bmi对象,不会把新的Bmi对象的值传递到People中去。

这样People中的this对象并没有得到增长,同时Bmi.call()返回的值也没有用到,所以People实例中就不会有sex、weight、height属性和getBmi()函数。

解决办法: 构造函数结合使用原型链或寄生组合:

function Bmi(sex, weight=1, height=1) {
  if (this instanceof Bmi) {
    this.sex = sex;
    this.weight = weight;
    this.height = height;
    this.getBmi = function() {
      return this.weight / (this.height ** 2);
    };
  } else {
    return new Bmi(sex);
  }
}

function People(height, weight) {
  Bmi.call(this, 'male');
  this.height = height;
  this.weight = weight;
}


People.prototype = new Bmi(); // 重点

var guy = new People(1.75, 68);
console.log(guy.sex) // "male"

这样写的话,一个People的实例同时也是一个Bmi的实例,所以Bmi.call()才会去执行,为People实例添加上属性和函数。


总结

当多个人一同构建一个项目时,作用域构安全函数就非常必要,对全局对象意外的更改可能就会导致一些常常难以追踪的错误,这和平常设置空变量和空函数一样避免因为其他人可能发生的错误而阻塞程序执行。

来源:https://www.cnblogs.com/hencins/archive/2018/11/30/10042752.html



站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: https://www.fly63.com/article/detial/1455

js在excel的编写_excel支持使用JavaScript自定义函数编写

微软 称excel就实现面向开发者的功能,也就是说我们不仅可以全新定义的公式,还可以重新定义excel的内置函数,现在Excel自定义函数增加了使用 JavaScript 编写的支持,下面就简单介绍下如何使用js来编写excel自定义函数。

Js call()理解

call()方法使用一个指定的this值和单独给出的一个或多个参数来调用一个函数,该方法的语法和作用与apply()方法类似,只有一个区别,就是call()方法接受的是一个参数列表,而apply()方法接受的是一个包含多个参数的数组。

javascript函数记忆

记忆的定义基本上描述了实施该技术的有用方案。 当您拥有一个昂贵的函数时,如果给定相同的参数,该函数将始终导致相同的值,则缓存结果并在下次调用它时返回缓存的值非常有效。 这样,宝贵的时间不会浪费在重新计算值上

js中HttpServletRequest的使用

HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息。

Js中的compose函数和pipe函数

compose函数可以将需要嵌套执行的函数平铺,嵌套执行就是一个函数的返回值将作为另一个函数的参数。我们考虑一个简单的需求:

云函数

云函数提供了一种 直接在云上运行,无状态的、短暂的、由事件触发的代码 的能力。ServerLess,即无服务器架构,也叫轻服务,它包含两个部分,如下:

js中(function(){…})()立即执行函数写法理解

( function(){…} )()和( function (){…} () )是两种javascript立即执行函数的常见写法,最初我以为是一个括号包裹匿名函数,再在后面加个括号调用函数,最后达到函数定义后立即执行的目的,后来发现加括号的原因并非如此

高阶函数

filter用于对数组进行过滤。 它创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。注意:filter()不会对空数组进行检测、不会改变原始数组

Js函数拓展

箭头函数的作用域永远是定义时的作用域,因此不受严格模式,call,bind,apply方法的影响。 想改变箭头函数作用域:将箭头函数定义在一个普通函数中。想改变这个普通函数作用域,我们可以改变箭头函数的作用域

CSS之calc()

calc() 函数支持任意CSS长度单位的混合计算,遵循标准数学运算优先级规则,可以动态计算长度值。注意,calc()函数内部的运算符两侧各加一个空白符,否则会产生解析错误。calc()使用的难点在于百分比

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!