一、安全的类型检测
①为什么引入安全的类型检测,难道javascript内置的类型检测不靠谱吗?
1) typeof 只能检测基本的数据类型,对于引用类型全部返回object,所以不靠谱吧
2) instanceof 是可以检测出引用类型,但是它在存在多个全局作用域的情况下,问题多多。
例如:var isArray = value instanceof Array; 只有value是一个数组,且value还必须和Array构造函数再同一个作用域下返回true。如果value是在另个frame中,那么返回的必定是false
②解决问题:大家知道,在任何值上调用Object 原生的toString()方法,都会返回一个[object NativeConstructorName]格式的字符串。每个类在内部都有一个[[Class]]属性,这个属性中就指定了上述字符串中的构造函数名。且由于原生数组的构造函数名与全局作用域无关,因此使用toString()就能保证返回一致的值。
function SafeTypeCheck() {
if (typeof this.isNumber != "function") {
// Number类型
SafeTypeCheck.prototype.isNumber = function (value) {
return Object.prototype.toString.call(value) == "[object Number]";
};
// String类型
SafeTypeCheck.prototype.isString = function (value) {
return Object.prototype.toString.call(value) == "[object String]";
};
// Boolean类型
SafeTypeCheck.prototype.isBoolean = function (value) {
return Object.prototype.toString.call(value) == "[object Boolean]";
};
// Array类型
SafeTypeCheck.prototype.isArray = function (value) {
return Object.prototype.toString.call(value) == "[object Array]";
};
// Function类型
SafeTypeCheck.prototype.isFunction = function (value) {
return Object.prototype.toString.call(value) == "[object Function]";
};
// RegExp类型
SafeTypeCheck.prototype.isRegExp = function (value) {
return Object.prototype.toString.call(value) == "[object RegExp]";
};
// Null类型
SafeTypeCheck.prototype.isNull = function (value) {
return Object.prototype.toString.call(value) == "[object Null]";
};
// Undefined类型
SafeTypeCheck.prototype.isUndefined = function (value) {
return Object.prototype.toString.call(value) == "[object Undefined]";
};
// Object类型, 包括JOSN类型
SafeTypeCheck.prototype.isObject = function (value) {
return window.JSON && Object.prototype.toString.call(value) == "[object Object]";
};
// Native JSON类型
// var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) == "[object JSON]";
}
}
// 测试
var checkType = new SafeTypeCheck();
alert(checkType.isFunction(function () {})); // true
alert(checkType.isArray([])); // true
alert(checkType.isString(123)); // false
二、作用域安全的构造函数
①引子:先看一个例子:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
}
var zhang = new Person("zhang", 34, "worker");
// alert(zhang.name); // zhang
// alert(window.name); // 为空
②正常情况下这是完全没有问题的,但当Person在实例化时被忘记了new了,那么可想而知,this指向了window
var li = Person("li", 20, "student");
alert(li.name); // 报错
alert(window.name); // li
③因此,我们在寻找一个良好的代码来屏蔽这样的问题
function Person(name, age, job){
// 当使用了new实例化时,this指向Person
if(this instanceof Person) {
this.name = name;
this.age = age;
this.job = job;
} else {
// 当忘记了new对象时,自动会加上一个new关键字,这样就避免了指向window的问题
return new Person(name, age, job);
}
}
var zhang = new Person("zhang", 34, "worker");
// alert(zhang.name); // zhang
// alert(window.name); // 为空
var li = Person("li", 20, "student");
alert(li.name); // li
alert(window.name); // 为空
④同样,安全作用域的构造函数也是有bug的,如果你想继承但只是用窃取模式,不是原型链继承模式时,会因为父类作用域被锁住而失败的现象
function Student(grade) {
Person.call(this, "wang", 29, "teacher");
this.grade = grade;
}
var stu = new Student(100);
// 由于父类的作用域被锁住,所以无法调用
alert(stu.name); // undefined
alert(stu.grade); // 100
⑤所以,我们没办法。只能老老实实的用原型链搞定它。
function Student(grade) {
Person.call(this, "wang", 29, "teacher");
this.grade = grade;
}
Student.prototype = new Person();
var stu = new Student(100);
// 这样就ok了
alert(stu.name); // wang
alert(stu.grade); // 100
三、惰性载入函数
①引子:为什么要有惰性载入函数?先看下面一个创建XHR对象的例子:
function createXHR(){
if (typeof XMLHttpRequest != "undefined"){ // 非ie
return new XMLHttpRequest();
} else if (typeof ActiveXObject != "undefined"){ // ie
if (typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp"],
i,len;
for (i=0,len=versions.length; i < len; i++){
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex){
//跳过
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error("No XHR object available.");
}
}
②问题所在:上述例子的功能是:针对不同的浏览器创建一个XHR对象,函数中有许多if语句,在代码执行期间我们需要判断。因为我们使用的浏览器不会发送变化,在这个函数第二次被调用的时候,我个人认为该函数的if语句就应该不会再次去判断是什么样的浏览器而去实例化不同的XHR对象。因此我们提出了惰性载入函数。
③应用:现在将上述的函数优化一下:
function createXHR(){
if (typeof XMLHttpRequest != "undefined"){ // 非ie
arguments.callee = function () {
return new XMLHttpRequest();
}
} else if (typeof ActiveXObject != "undefined"){ // ie
arguments.callee = function () {
if (typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp"],
i,len;
for (i=0,len=versions.length; i < len; i++){
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex){
//跳过
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
} else {
arguments.callee = function () {
throw new Error("No XHR object available.");
}
}
return arguments.callee();
}
alert(new createXHR() instanceof XMLHttpRequest); // true
④优点:这样,我们在调用时,就不需要执行了if语句,这又提高了效率。
四、函数绑定
①引子:先看下面一个例子:
var handler = {
message: "Event handled",
handleClick: function(event){
alert(this.message);
}
};
var message = "window";
var btn = document.getElementById("btn");
EventUtil.addEvent(btn, "click", handler.handleClick); // undefined,less ie8弹出window
②原因:为什么会弹出undefined?在于没有保存handler.handleClick()的环境,所以this 对象最后是指向了DOM 按钮而非handler(在IE8 中)。因此我们可以用闭包来保留handler.handleClick()的环境,起码在这个类中中。this 指向window。)
var handler = {
message: "Event handled",
handleClick: function(event){
alert(this.message);
}
};
var message = "window";
var btn = document.getElementById("btn");
EventUtil.addEvent(btn, "click", function(event){
handler.handleClick(event); // Event handled
});
③解决方案:虽然可以用闭包解决此类问题,但是使用闭包会使代码变得很难理解和调试.我们可以用apply或者call方法改变作用域来实现绑定。
EventUtil.addEvent(btn, "click", function () {
return handler.handleClick.call(handler);
});
④封装成函数:
var handler = {
message: "Event handled",
handleClick: function(event){
alert(this.message);
}
};
var message = "window";
function bind(fn, context) {
return function () {
fn.call(context, arguments);
}
}
var btn = document.getElementById("btn");
EventUtil.addEvent(btn, "click", bind(handler.handleClick, handler));
⑤ECMAScript 5 为所有函数定义了一个原生的bind()方法,进一步简单了操作.
EventUtil.addEvent(btn, "click", handler.handleClick.bind(handler));
五、函数柯里化.
1.含义:调用另一个函数,并为它传入要柯里化的函数和参数。以下函数能很好的展示库里化的概念。
function add(num1, num2){
return num1 + num2;
}
function curriedAdd(num2){
// 调用了add()函数,并为它传入了参数
return add(5, num2);
}
alert(add(2, 3)); //5
alert(curriedAdd(3)); //8
2.创建函数库里化的通用方式
function curry(fn){
// 取得外部函数的从第二个开始的所有参数
var args = Array.prototype.slice.call(arguments, 1);
// alert(args); // 10, 10, 10
return function(){
// 取得内部函数(匿名函数)的全部参数
var innerArgs = Array.prototype.slice.call(arguments);
// alert(innerArgs); // 15, 15
// 将外部的参数和内部参数全部聚集在一起
var finalArgs = args.concat(innerArgs);
// alert(finalArgs); // 10, 10, 10, 15, 15
// 最后将所有的传入到要调用的函数中去
return fn.apply(null, finalArgs);
};
}
function add(){
var res = 0;
for(var i = 0, len = arguments.length; i < len; i++) {
res += arguments[i];
}
return res;
}
// 对于下面的函数调用
// fn为add函数
// args为10, 10, 10
// innerArgs为15, 15
var curried = curry(add, 10, 10, 10);
alert(curried(15, 15)); // 60
3.上述的curry函数没用改变相应的作用域,我们可以继续修改
function curry(fn, context){
// 取得外部函数的从第三个开始的所有参数
var args = Array.prototype.slice.call(arguments, 2);
// alert(args); // btn
return function(){
// 取得内部函数(匿名函数)的全部参数
var innerArgs = Array.prototype.slice.call(arguments);
// alert(innerArgs); // [object MouseEvent]
// 将外部的参数和内部参数全部聚集在一起
var finalArgs = args.concat(innerArgs);
alert(finalArgs); // btn, [object MouseEvent]
// 最后将所有的传入到要调用的函数中去
return fn.apply(context, finalArgs);
};
}
var handler = {
message: "Event handled",
handleClick: function(name, event){
alert(this.message + ":" + name + ":" + event.type);
}
};
var btn = document.getElementById("btn");
EventUtil.addEvent(btn, "click", curry(handler.handleClick, handler, "btn"));
4.ECMAScript 5 的bind()方法也实现函数柯里化,只要在this 的值之后再传入另一个参数即可。
var handler = {
message: "Event handled",
handleClick: function(name, event){
alert(this.message + ":" + name + ":" + event.type);
}
};
var btn = document.getElementById("btn");
EventUtil.addEvent(btn, "click", handler.handleClick.bind(handler, "btn"));
高级函数
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- 使用 __slots__ 限制实例属性, 比如,只允许对Student实例添加name和age属性。 然后,我们试...
- 惰性函数很好理解,假如同一个函数被大量范围,并且这个函数内部又有许多判断来来检测函数,这样对于一个调用会浪费时间和...
- 高阶函数,又称算子(运算符)或泛函,包含多于一个箭头的函数,高阶函数是至少满足下列一个条件的函数:1.接受一个或多...