基础部分

构造函数本质上就是一个使用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实例添加上属性和函数。

总结

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

更多专业前端知识,请上 【猿2048】www.mk2048.com

如何写一个作用域安全的构造函数相关推荐

  1. 手写一个promise用法_手写一个 Promise

    1 js 的基本数据类型? 2 JavaScript 有几种类型的值? 3 什么是堆?什么是栈?它们之间有什么区别和联系? 4 内部属性 [Class] 是什么? 5 介绍 js 有哪些内置对象? 6 ...

  2. 面试题:你能写一个Vue的双向数据绑定吗?

    在目前的前端面试中,vue的双向数据绑定已经成为了一个非常容易考到的点,即使不能当场写出来,至少也要能说出原理.本篇文章中我将会仿照vue写一个双向数据绑定的实例,名字就叫myVue吧.结合注释,希望 ...

  3. 面试题:你能写一个 Vue 的双向数据绑定吗?

    作者:呆头呆脑丶 segmentfault.com/a/1190000014274840 在目前的前端面试中,vue的双向数据绑定已经成为了一个非常容易考到的点,即使不能当场写出来,至少也要能说出原理 ...

  4. 写一个Vue的双向数据绑定

    原文 https://segmentfault.com/a/1190000014274840 在目前的前端面试中,vue的双向数据绑定已经成为了一个非常容易考到的点,即使不能当场写出来,至少也要能说出 ...

  5. 你能写一个 Vue 的双向数据绑定吗?

    在目前的前端面试中,vue的双向数据绑定已经成为了一个非常容易考到的点,即使不能当场写出来,至少也要能说出原理.本篇文章中我将会仿照vue写一个双向数据绑定的实例,名字就叫myVue吧.结合注释,希望 ...

  6. 【干货】JDK动态代理的实现原理以及如何手写一个JDK动态代理

    动态代理 代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足轻重的地位.代理模式从类型上来说,可以分为静态代理和动态代理两种类型. 在解 ...

  7. 【React组件】写一个模仿蓝湖的图片查看器

    前言 最近公司让写一个可以自由拖拽放大的图片查看器,我寻思这还不简单,一顿操作猛如虎,俩小时后: 事实证明,一旦涉及到 DOM 的变换操作,如果很多细节考虑不全,抓过来就写,那基本就凉了.于是我仔细分 ...

  8. ipad php mysql_如何用PHP/MySQL为 iOS App 写一个简单的web服务器(译) PART1

    原文:http://www.raywenderlich.com/2941/how-to-write-a-simple-phpmysql-web-service-for-an-ios-app 作为一个i ...

  9. Spring Security 实战干货:从零手写一个验证码登录

    1. 前言 前面关于Spring Security胖哥又写了两篇文章,分别图文并茂地介绍了UsernamePasswordAuthenticationFilter和 AuthenticationMan ...

最新文章

  1. Google AI骗过了Google,工程师竟无计可施?
  2. php通过http请求发送数组
  3. 在Kubernetes上运行SAP UI5应用(下): 一个例子体会Kubernetes内容器的高可用性和弹性伸缩...
  4. html中的expand属性,expand的用法总结大全
  5. codesys 简单案例_第一章:初识Codesys-1.4从一个示例程序讲起
  6. Web笔记-移动前端开发笔记
  7. MySql应用原理分析系列文章目录
  8. 2. APIS官网剖析(博主推荐)
  9. 学习Java必须避开的十大致命雷区,新手入门千万不要踩!
  10. OpenGL与gl glu glut freeglut glew glfw封装库关系(十五)
  11. 海归博士程序员光鲜背后:下车间写代码,体验炼钢灼人的热度
  12. 【LaTeX入门】软件安装
  13. 灵雀云Kube-OVN:基于OVN的开源Kubernetes网络实践
  14. populate auto detected configs
  15. HTML入门学习笔记+详细案例
  16. 自动发票校验 Auto Invoice Verification
  17. CStyle足迹:一个BIOS人的成长日记之开篇
  18. curl:Failed connect to github-production-release-asset-2e65be.s3...; Connection refused解决办法
  19. html5圣诞贺卡,用CorelDRAW制作漂亮别致的圣诞贺卡
  20. 张量分解和应用(1)

热门文章

  1. jwt私钥和公钥怎么获取_jwt 用rsa公钥私钥进行验证(python发送,java接受)
  2. 群晖备份linux分区,数据丢失的后悔药,群晖NAS备份方案详解
  3. MySQL在哪里看secret_key_K8S 创建和查看secret(九)
  4. python保存代码_python入门(5)使用文件编辑器编写代码并保存执行
  5. 描述一下JVM加载class文件的原理机制
  6. Spring Boot 学习笔记(三)Spring boot 中的SSM
  7. 史上最简单的SpringCloud教程 | 第二篇: 服务消费者(rest+ribbon)(Finchley版本)
  8. php关于ob_start('ob_gzhandler')启用GZIP压缩的bug
  9. 埃氏筛法的一般写法(区间筛法)
  10. 动画原理——绘制正弦函数环绕运动椭圆运动