安全的类型检测
typeof操作符
检测数据类型的结果可能会不正确;
instanceof操作符
操作符在多个全局作用域下存在问题:
var value = [];
var isArray = value instanceof Array;
console.log(isArray);
上述代码都在全局作用域,返回true;但如果value在其他frame中,则返回false。
JSON对象
该对象也难以确定是否为原生对象;
解决办法:
因为在任何值上调用Object原生的toString()方法,都返回一个[Object NativeConstructorName]格式的字符串。每个类在内部都有一个[[Class]]属性,这个属性指定了上述字符串中构造函数的函数名,如:
console.log(Object.prototype.toString()); //[object Object]
//利用call():
var value = [];
console.log(Object.prototype.toString.call(value)); //[object Array]
//进一步完善:
function whichType (value) {
console.log(Object.prototype.toString.call(value));
}
whichType("Obj"); //[boject String]
whichType(["hello"]); //[object Array]
whichType(321); //[object Number]
//检测是否为函数:
function isFunction (value) {
return Object.prototype.toString.call(value) == "[object Function]";
}
console.log(isFunction(Object.prototype.toString)); //true
//检测原生JSON对象:
console.log(window.JSON && Object.prototype.toString.call(JSON) == "[object JSON]");
不适用于IE中COM对象形式实现的函数
作用域安全的构造函数
当使用new调用构造函数时,构造函数内用到的this对象会指向新创建的对象实例,如:
function Person (name) {
this.name = name;
}
var person = new Person("oliver");
console.log(person.name);
问题是当没有使用new操作符,直接调用构造函数,this会映射到全局对象window上,导致错误对象属性的意外增加:
function Person (name) {
this.name = name;
}
var person = Person("oliver");
console.log(window.name); //oliver
解决办法是创建一个作用域安全的构造函数:
function Person(name) {
if (this instanceof Person) { //如果this是Person的实例
this.name = name;
} else {
return new Person(name); //否则调用new操作符
}
}
var person1 = Person("oliver");
console.log(person1.name); //oliver
var person2 = new Person("troy");
console.log(person2.name); //troy
console.log(window.name); //""
但是,如果使用构造函数窃取模式的继承且不实用原型链,那么这个继承很可能被破坏如:
function Person(name) {
if (this instanceof Person) { //如果this是Person的实例
this.name = name;
} else {
return new Person(name); //否则调用new操作符
}
}
function People (name,age) {
Person.call(this, name);
this.age = age;
}
var p = new People("Oliver", 18);
console.log(p.name); //undefined
console.log(p.age); //18
结合使用原型链或者寄生组合则可以解决这个问题:
function Person(name) {
if (this instanceof Person) { //如果this是Person的实例
this.name = name;
} else {
return new Person(name); //否则调用new操作符
}
}
function People (name,age) {
Person.call(this, name);
this.age = age;
}
People.prototype = new Person(); //关键点
var p = new People("Oliver", 18);
console.log(p.name); //Oliver
console.log(p.age); //18
惰性载入函数
惰性函数就是函数执行的分支仅会发生一次。
第一种
就是在函数被调用时再处理函数:
function createXHR () {
if (typeof XMLHttpRequest !== "undefined") {
createXHR = function () { //关键点
return new XMLHttpRequest();
}
} else if (typeof ActiveXObject !== "undefined") {
createXHR = function () { //关键点
return new ActiveXObject(["MSXML2.XMLHttp"]);
}
} else {
createXHR = function () { //关键点
throw new Error("No XHR object available.");
}
}
return createXHR(); //关键点
}
第二种
就是指定适当的函数:
function createXHR () {
if (typeof XMLHttpRequest !== "undefined") {
return function () { //关键点
return new XMLHttpRequest();
}
} else if (typeof ActiveXObject !== "undefined") {
return function () { //关键点
return new ActiveXObject(["MSXML2.XMLHttp"]);
}
} else {
return function () { //关键点
throw new Error("No XHR object available.");
}
}
return createXHR(); //关键点
}
函数绑定
函数绑定要创建一个函数, 可以在特定的this环境中以指定参数调用另一个函数。 该技巧常常和回调函数与事件处理程序一起使用, 以便在将函数作为变量传递的同时保留代码的执行环境。 由于代码之中存在着this变量, 而this在当前环境下指向确定的对象, 但是当更改代码的执行环境时, 就会出现问题了。 为了解决这个问题, javascript函数库中实现了一个bind() 函数来解决这个问题。
一个简单的bind() 函数接收一个函数和一个环境, 并返回一个在给定环境中调用给定函数的函数, 并且将所有参数原封不动传递过去。 语法如下:
function bind(fn, context) {
return function() {
return fn.apply(context, arguments);
}
}
注意这里使用的arguments并不是bind() 的, 是内部函数的。
var handler = {
message: "Event handled",
handleClick: function(event) {
alert(this.message);
}
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler));
ECMAScript5为所有函数定义了一个原生的bind() 方法, 进一步简化了操作。
var handler = {
message: "Event handled",
handleClick: function(event) {
alert(this.message);
}
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler));
它们主要用于事件处理程序以及setTimeout() 和setInterval()。 然而被绑定函数与普通函数相比有更多的开销, 它们需要更多内存, 同时也因为多重函数调用稍微慢一些, 所以最好只在必要时使用。
函数柯里化
它用于创建已经设置好了一个或多个参数的函数。 函数柯里化的基本方法是: 使用一个闭包返回一个函数。 当函数被调用时, 返回的函数还需要设置一些传入的参数。
柯里化函数通常由以下步骤动态的创建: 调用另一个函数并为它传入要柯里化的函数和必要参数。 下面是创建柯里化函数的通用方式:
function curry(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);
}
}
这种变化也需要额外的开销
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。