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

时间: 2018-11-30阅读: 1191标签: 函数

基础部分

构造函数本质上就是一个使用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.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

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

注入eval, Function等系统函数,截获动态代码

现在很多网站都上了各种前端反爬手段,无论手段如何,最重要的是要把包含反爬手段的前端javascript代码加密隐藏起来,然后在运行时实时解密动态执行。动态执行js代码无非两种方法,即eval和Function。

JS高阶函数reduce()的常用场景

reduce()语法:必需。初始值, 或者计算结束后的返回值。 数组求和;数组最大值;数组去重;计算数组中每个元素的出现的次数

JavaScript高阶函数

Javascript中的函数本质上都指向某个变量,既然变量可以指向函数,函数的参数可以接受变量,那么函数是不是可以可以作为另一个函数的入参?因为Javascript是一门弱类型语言,不会对函数输入值和函数的输出值进行强定义和类型检查

JS 高阶函数

什么是函数?在大多数编程语言中,函数是一段独立的代码块,用来抽象处理某些通用功能的方法;主要操作是给函数传入特定对象(参数),并在方法调用结束后获得一个新的对象

js中sort函数用法总结_sort排序算法原理

js中sort方法用于对数组的元素进行排序,并返回数组。默认排序顺序是根据字符串Unicode码点。如果要得到自己想要的结果,不管是升序还是降序,就需要提供比较函数了。该函数比较两个值的大小,然后返回一个用于说明这两个值的相对顺序的数字

理解 JavaScript Mutation 突变和 PureFunction 纯函数

不可变性、纯函数、副作用,状态可变这些单词我们几乎每天都会见到,但我们几乎不知道他们是如何工作的,以及他们是什么,他们为软件开发带来了什么好处。在这篇文章中,我们将深入研究所有这些,以便真正了解它们是什么以及如何利用它们来提高我们的Web应用程序的性能。

css中calc()函数

css3中函数:用于动态计算长度值。(注意事项:运算符前后都需要保留一个空格,例如:width: calc(100% - 10px));任何长度值都可以使用calc()函数进行计算;

JS函数的三种方式

函数,一段能够自动完成某些功能的代码块,函数的出现,既解决了重复使用重一功能的需求,又可以避免代码的臃肿性。使用函数有两个要求:必须调用后才可以执行;函数名不要和关键字以及系统函数相同;

js类的constructor中不支持异步函数吗?

如果是普通函数,可以用async 和await来解决你的问题。但你这个是在constructor里,constructor 的作用是返回一个对像实例,如果加了async就变成返回一个promise了,所以这个方法行不通

关于Javascript中的valueOf与toString

以上引申出对象数据的转换的问题:所有对象继承了两个转换方法:toString(): 它的作用是返回一个反映这个对象的字符串,valueOf():它的作用是返回它相应的原始值

点击更多...

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