[Effective JavaScript 笔记]第33条:使构造函数与new操作符无关
当使用函数作为一个构造函数时,程序依赖于调用者是否记得使用new操作符来调用该构造函数。注意:该函数假设接收者是一个全新的对象。
一个例子
function User(name,pwd){
this.name=name;
this.pwd=pwd;
}
当调用者,忘记使用new关键字时,那么这个函数的接收者是全局对象。
var u=User('wengxuesong','asdfasdfadf');
u;//undefinedthis.name;//'wengxuesong'this.pwd;//'asdfasdfadf'
该函数返回了无意义的undefined,还会修改全局对象。
如果将User函数应用ES5的严格模式,那么它的接收者为undefined
function User(name,pwd){
'use strict';
this.name=name;
this.pwd=pwd;
}
var u=User('wengxuesong','asdfasdfadf');//error:this is undefined
上面代码会报错,因为在严格模式下直接运行的函数中的this是undefined,无法给undefined定义属性。
如何才能使User函数在不使用new操作符的情况下,还是按预期工作呢?提供一个不管怎样调用都工作如构造函数的函数。实现该函数的一个简单方法是检查函数的接收者是否是正确的User实例。
function User(name,pwd){
if(!(this instanceof User)){
return new User(name,pwd);//额外的函数调用
}
this.name=name;
this.pwd=pwd;
}
使用这种方式无论如何调用User函数,都会返回一个继承自User.prototype的对象
var x=User('wengxuesong','12123123');
var y=new User('songqiang','sfasdfasdf');
x instanceof User;//true
y instanceof User;//true
这个模式的一个缺点是它需要额外的函数调用,因此代价有点高。而且,很难适用于可变参数函数,因为没有一种直接模拟apply方法将可变参数函数作为构造函数调用的方式。
function User(name,pwd){
var self=this instanceof User?this:Object.create(User.prototype);
self.name=name;
self.pwd=pwd;
return self;
}
Object.create需要一个原型对象作为参数,并返回一个继承自该原型对象的新对象。因此,当以函数的方式调用该版本的User函数时,结果将返回一个继承自User.prototype对象的新对象,并且该对象具有已经初始化的name和pwd属性。
Object.create只有在ES5环境中才是有效的,可以通过代码进行兼容。上节已经写过一次了,再写一遍加深印象吧。
if(typeof Object.create === 'undefined'){
Object.create=function(prototype){
function F(){};
F.prototype=prototype;
return new F();
}
}
注意:这里只实现了单参数的Object.create函数。真实版本的Object.create函数还接受一个可选的参数,该参数描述了一组定义在新对象上的属性描述符。
如果使用new操作符调用新版本的User参数会发生什么?
构造函数覆盖模式,使用new操作符调用该函数的行为就如以函数调用它的行为一样。这能工作完全利益于js允许new表达式的结果可以被构造函数中的显式return语句所覆盖。当User函数返回self对象时,new表达式的结果就变为self对象。该self对象可能是另一个绑定到this的对象。
防范误用构造函数可能并不是太值得去做,尤其是当仅仅是局部使用构造函数时。但是理解如果以错误的方式调用构造函数会造成严重后果很重要。至少文档化构造函数期望使用new操作符调用是很重要的,尤其是在跨大型代码库中其享构造函数或该构造函数来自一个共享库时。
提示
通过使用new操作符或Object.create方法在构造函数定义中调用自身使得该构造函数与调用语法无关
当一个函数期望使用new操作符调用时,清晰地文档化该函数
附录一:Object.create方法
以下内容来自Mozilla 开发者社区
Object.create()方法创建一个拥有指定原型和若干指定属性的对象。
语法
Object.create(原型对象,[属性对象集])
异常
如果原型对象不是null或一个对象值,则抛出一个TypeError异常
实现类式继承
单继承
function Shape(){
this.x=0;
this.y=0;
}
Shape.prototype.move=function(x,y){
this.x+=x;
this.y+=y;
console.info('Shape moved.');
};
function Rectangle(){
Shape.call(this);//构造函数借用
}
Rectangle.prototype=Object.create(Shape.prototype);
var rect=new Rectangle();
rect instanceof Rectangle;//true
rect instanceof Shape;//true
rect.move(1,1);//"Shape moved"
画张图表示一下上面的关系
多继承
function MyClass(){
SuperClass.call(this);
OtherSuperClass.call(this);
}
MyClass.prototype=Object.create(SuperClass.prototype);
mixin(MyClass.prototype,OtherSuperClass.prototype);//mixin
MyClass.prototype.myMethod=function(){
};
propertyObject参数
var o;
//创建原型为null的空对象
o=Object.create(null);
o={};
//以字面量方式创建空对象相当于下面这句代码
o=Object.create(Object.prototype)
o=Object.create(Object.prototype,{
//创建对象的数据属性
foo:{writable:true,configurable:true,value:'hello'},
//创建对象的访问器属性
bar:{
configurable:false,
get:function(){return 10;},
set:function(val){console.log('setting "o.bar" to',val);}
}
});
function Constructor(){}
o=new Constructor();
//相当于
o=Object.create(Constructor.prototype);
//如果Constructor里有一些初始化代码,Object.create不能执行那些代码
//应该相当于
o={};
o=Object.create(Constructor.prototype);
Constructor.call(o,args);
//创建一个以另一个空对象为原型,且拥有一个属性p的对象
o=Object.create({},{p:{value:42}});
//省略了的属性默认为false,所以属性p是不可写,不可枚举,不可配置的
o.p=24;
o.p;//42
o.q=12;
for(var prop in o){
console.log(prop);
}
//'q'delete o.p;//false
//创建一个可写的,可枚举的,可配置的属性p
o2=Object.create({},{p:{value:42,writable:true,enumerable:true,configurable:true}});
兼容版
if(typeof Object.create !== 'function'){
Object.create=(function(){
function NOP(){}
var hasOwn=Object.prototype.hasOwnProperty;
return function(o){
//1、如果o不是Object或null,抛出一个类型错误异常
if(typeof o!=='object'){
throw TypeError('Object prototype may only be an Object or null.');
}
//2、使创建的一个新的对象为obj
//3、设置obj的内部属性[[Prototype]]为o
NOP.prototype=o;
var obj=new NOP();
NOP.prototype=null;//解除NOP函数的prototype的引用
//4、如果参数有Properties,那么检测并添加属性到obj上。
if(arguments.length>1){
var Properties=Object(arguments[1]);
for(var prop in Properties){
if(hasOwn.call(Properties,prop)){
obj[prop]=Properties[prop];
}
}
}
return obj;
}
})();
}
[Effective JavaScript 笔记]第33条:使构造函数与new操作符无关的更多相关文章
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- [Effective JavaScript 笔记]第38条:在子类的构造函数中调用父类的构造函数
示例 场景类 场景图(scene)是在可视化的过程中(如游戏或图形仿真场景)描述一个场景的对象集合.一个简单的场景包含了在该场景中的所有对象(称角色),以及所有角色的预加载图像数据集,还包含一个底层图 ...
- [Effective JavaScript 笔记]第52条:数组字面量优于数组构造函数
js的优雅很大程序要归功于程序中常见的构造块(Object,Function及Array)的简明的字面量语法.字面量是一种表示数组的优雅方法. var a=[1,2,3,5,7,8]; 也可以使用构造 ...
- [Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑
构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.t ...
- [Effective JavaScript 笔记]第65条:不要在计算时阻塞事件队列
第61条解释了异步API怎样帮助我们防止一段程序阻塞应用程序的事件队列.使用下面代码,可以很容易使一个应用程序陷入泥潭. while(true){} 而且它并不需要一个无限循环来写一个缓慢的程序.代码 ...
- [Effective JavaScript 笔记]第54条:将undefined看做“没有值”
undefined值很特殊,每当js无法提供具体的值时,就会产生undefined. undefined值场景 未赋值的变量的初始值即为undefined. var x; x;//undefined ...
随机推荐
- 基于int的Linux的经典系统调用实现
先说明两个概念:中断和系统调用 一 系统调用: 是应用程序(运行库也是应用程序的一部分)与操作系统内核之间的接口,它决定了应用程序是如何和内核打交道的. 1, Linux系统调用:2.6.19版内 ...
- 【POJ各种模板汇总】(写在逆风省选前)(不断更新中)
1.POJ1258 水水的prim……不过poj上硬是没过,wikioi上的原题却过了 #include<cstring> #include<algorithm> #inclu ...
- AngularJS开发指南12:AngularJS的模板,CSS,数据绑定详解
模板 AngularJS模板是一种声明式的规则.它包含了模型和控制器的信息,最后会被渲染成用户在浏览器中看到的视图.它是静态的DOM,包含HTML,CSS和AngularJS指定的元素和属性.Angu ...
- JS事件学习笔记(思维导图)
导图
- navigationBar设置透明度
将NavigationBar设置透明(仅将指定视图控制器进行透明处理),步骤如下:1.在视图控制器的头文件中实现UINavigationControllerDelegate,例如:@interface ...
- Android Studio快速添加Gson以及GsonFormat的使用
目录: 一.Android Studio快速添加Gson 二.Android Studio中GsonFormat的使用 三.在线JSON校验格式化工具 一.Android Studio快速添加Gson ...
- 理解jar
对于大多数 Java 开发人员来说,JAR 文件及其 “近亲” WAR 和 EAR 都只不过是漫长的 Ant 或 Maven 流程的最终结果.标准步骤是将一个 JAR 复制到服务器(或者,少数情况下是 ...
- maven初学(三) SNAPSHOT
使用场景: 通常一个大型软件是由多个模块组成的,不同的组使用相同应用的不同版本. 在开发阶段,可能经常需要修复bug和优化. 这种情况下就会导致其他组频繁更新代码和pom文件 SANPSHOT SNA ...
- python 学习笔记1(序列;if/for/while;函数;类)
本系列为一个博客的学习笔记,一部分为我原创. 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 1. print 可以打印 有时需要 ...
- 蝙蝠算法-python实现
BAIndividual.py import numpy as np import ObjFunction class BAIndividual: ''' individual of bat algo ...