使用typeof检测,只能区分基本类型的具体类型,而对于引用类型只能统一返回object。而使用Object.prototype.toString.call(value)能够确切返回基本、引用类型的数据的具体类型,只要这个数据的构造函数是原生而非自定义的。
官方文档中对这个方法的描述是:
When the toString method is called, the following steps are taken:
If the this value is undefined, return “[object Undefined]“.
If the this value is null, return “[object Null]“.
1. Get the [[Class]] property of this object.
2. Compute a string value by concatenating the three strings “[object “, Result (1), and “]”.
3. Return Result (2)
每个类内部都有一个class属性,就是原生构造函数名。调用这个方法的时候会去查找这个属性。
在对对象的属性进行任何更改之前,首先确定this是否指向正确的对象,如果不是,则创建一个新的实例并返回。这样做的目的在于如果没有使用new 调用构造函数,那么this指向window,构造函数中的属性名可能会和window对象的属性同名而造成覆盖:
function Person(name,age,gender){
if(this instanceof Person){
this.name=name;
this.age=age;
this.gender=gender
}else{
return new Person(name,age,gender);
}
}
这样,用下面两种方式调用,都可以得到想要的结果:
var person1=new Person('mike',20,'male');
alert(person1.name);//mike
var person2=Person('lily',10,'female');
alert(person2.name);//ily
避免了意外向全局对象上设置属性。
但是这样的方式在使用构造函数继承的时候会出现问题,但是搭配原型链继承就不会了(instanceof对祖先类进行检测也是返回true)。
function Chinese(province,name,age,gender){
if(this instanceof Chinese){
Person.call(this,name,age,gender); //构造函数继承
this.province=province;
}else{
return new Chinese(province,name,age,gender);
}
}
Chinese.prototype=new Person(); //原型链继承
var c1 = new Chinese("sichuan","lily",10,"female");
alert(c1 instanceof Chinese); //true
alert(c1 instanceof Person); //true
再来回顾一遍原型和构造函数组合的继承方式是什么样的过程:
下面是一幅原型继承的图解:
通过原型链继承,子类的原型对象就是一个父类的实例,它可以拿到父类实例中的所有属性,包括:
指向父类原型对象的prototype指针—>构成原型链
父类的实例属性
而通过构造函数继承,子类获得了实例属性,包括:
父类的实例属性—>覆盖掉通过原型方式继承的父类实例属性
这样,再加上子类自己的原型属性(方法)和实例属性(方法),就得到了一个完整的子类。
需要注意的一点是通过原型继承,SubType.prototype.constructor==SuperType,可以进行更改:
SubType.prototype.constructor==SubType
充分利用函数名也是一个指针,在第一次运行的时候就指向真确的if判断分支,下次再运行的时候就不必再进行一次if判断,而是直接执行分支中的内容。
就是想在某个this环境中持有另一个this环境中的变量,也就是说在某个A- this所在的代码环境中的某一段,要更改this为B-this。
一种方法是利用闭包屏蔽掉外部函数的this,但闭包层次多了代码不好理解。还有一种方法是使用bind来保持this上下文。
函数绑定的用途:函数作为参数传递,同时函数又必须在特定的环境中执行,常用于事件处理程序和setTimeout等函数中。
ar handler={
message:"event handler",
handlerClick:function(event){
alert(this.message);
}
}
btn.addEventListener('click',handler.handlerClick.bind(handler));
首先看一个概念,函数的内置对象arguments并不是一个真正的数组对象,使用:
Array.prototype.slice.call(arguments,0)
可将具有length属性的对象转换为数组对象。直接使用arguments.slice会报错。
函数柯里化就是一个函数内部返回一个闭包,并且返回的函数在调用的时候还需要传入一些参数。形式如下:
function curry(fn){
var args=Array.prototype.slice.call(arguments,1);
return function(){
var innerArgs=Array.prototype.slice.call(arguments,0);
var finnalArgs=args.concat(innerArgs);
return fn.apply(null,finnalArgs); //还未制定特定的环境
}
}
JS的bind方法也实现了柯里化,只需在参数this之后传入别的参数就行。这常用于给事件处理程序传入除了event之外的参数。
函数柯化有什么用呢,查看了张鑫旭大牛的博客,上面总结了三个好处:
var addEvent = (function(){
if (window.addEventListener) {
return function(el, sType, fn, capture) {
el.addEventListener(sType, function(e) {
fn.call(el, e);
}, (capture));
};
} else if (window.attachEvent) {
return function(el, sType, fn, capture) {
el.attachEvent("on" + sType, function(e) {
fn.call(el, e);
});
};
}
})();
Function.prototype.bind = function(context) {
var _this = this,
_args = Array.prototype.slice.call(arguments, 1);
return function() {
return _this.apply(context, _args.concat(Array.prototype.slice.call(arguments)))
}
}
取得自定义的属性值使用getAttribute(),而元素内置的属性,例如style、id等就直接使用ele.arrName来获取。