javascript数据类型/对象/函数/闭包/类型检测

0、数据类型:五种基本类型(string、number、boolean、null、undefined),对象(包括array、function、date、其他普通对象等)。
1、 包装对象:五种基本类型中的string、number、boolean都是有对应的包装对象的,如使用new String("string")可以创建真正的对象,而直接的string类型则在使用其属性方法时会创建临时的包装对象,属性可读不可写,使用完后随 即销毁。
2、对象、原型链、函数
a、对象:所有的对象都有proto标签,如果是字面量创建,则此标签指 向Object的prototype属性(注意,所有的function对象,都会有一个prototype属性,这个属性本身是个对象),而如果是使用 构造器创建(字面量创建实际上是用的new Object()这个构造器),则其proto标签会指向构造器(是个函数)的prototype这个对象属性(不要将prototype、proto混 淆)
对于function,new function name()是在创建对象,对象与function的联系是function的prototype对象在创建的对象的第一级原型链上,可以在这个 prototype上添加各种属性,这样这些属性在new出来的对象中是可用可见的(不可以在new出来的对象上更改,这是因为一旦用了 obj.property就会在这个new出来的对象里添加这个property,访问、修改都不会触碰到原型链上的同名property了)。而 functionname()则是实实在在的调用这个函数啦!注意,用function构造器构造对象的时候,function本身是怎么写的与对象毫无 关系,只有function的prototype才与这个对象有关系!
Object.create(object)来创建对象,得到的对象的proto指向object而不是这个对象本身是object,这就意味着有一个构造函数的prototype属性是这个object?这个函数是谁?在哪里?
b、函数
tip1:函数默认有arguments这个数组存储形参列表,当参数个数超过形参列表时,可以在arguments中获取实参。
tip2:参数是基本类型的时候,是值传参,是对象类型的时候(如数组),是引用传参。
三种构建函数的方式:
1、函数声明
2、实例化Function对象
3、函数表达式:var statement = function(){}
js在解析代码的时候,不是傻呼呼的直接一句一句运行,而是会按照作用域(js只有函数作用域)将函数声明、变量声明提前,上述三种方式中,只有第一种会被提前,这些提前的变量只是在js代码最开头占个坑,即使我们在代码中赋了值,此时在最开头的生命中都是undefined(函数的话只是记录个名字),变量声明完毕后,会逐行执行代码,当遇到函数调用(注意是调用!)时,会为这个函数(如果能查到已经声明了的话)初始化一个环境,环境内部的变量外部不可见,这就是函数作用域,函数环境构造的过程和外面这一层是一样的,又是从头声明变量(包括形参、内部声明的变量)、执行代码。
this
函数会自带this变量,总体来说,谁调用,this指向谁,obj.function()则指向obj,无人调用,指向window(其实是window在调用),用构造函数new的话,this指向new出来的实例。
闭包
定义:闭包是一个这样的代码块(作用域),它包含所谓的自由变量,也就是这些变量既没有定义在全局作用域中,也没有代码块中,在js中,由于只有函数作用域,故而这个代码块就是一个函数体。所以js中的闭包涉及到闭包作用域也涉及到为这些自由变量提供数据绑定的环境,也就是代码块的外部作用于
闭包,牢牢把握住作用域的概念,内部的函数引用外层作用域的变量,内部函数在外层作用域的外部被调用,那么这个内部函数所引用的变量不会因为他所在的外层函数调用完成而被释放。
经典例子:
在ul内部的li上添加处理函数,要求点击第i个li时alert出i,如果在for循环里绑定事件,结果都是alert处li的总数,为什么i没有被保存在handle函数中呢?这个定义在内部的匿名的handle函数不算是闭包吗?
答案是,这个匿名函数是个闭包,但是它执行的时机是什么时候呢,是for完成之后,这个闭包引用的变量i确实没有被释放,但是变成了循环之后的值,所以,对于每一个绑定的函数,它们引用的外部变量是一样的,都是外层函数那个i,始终是同样的值。
因此,我们把绑定处理函数(addEventListener)放到一个立即执行函数中来,在这个立即执行函数中,引用了外层函数的i,每次循环的i都不一样,又因为立即执行函数中有声明的匿名事件处理函数引用了这个变量,所以这个变量不会被释放,回过头来再看,每一个匿名的事件处理函数引用的外层变量都是不一样的,它们引用的是每次循环保存在各自立即执行函数中的i。
普通的函数与构造函数,这是两种用途上的概念,在写法上没有区别,使用的时候,前者直接调用,后者使用关键字new,但如果构造函数中除了各个属性以外有可执行的语句,在new的时候依然会执行;普通函数调用赋值给变量时,变量的到函数的return的值,但new则返回函数对象;构造函数的本质是把函数当成一个对象的模板,产生对象,所有对象的实例的原型都指向这个函数的prototype,prototype的constructor正是函数本身。
从上面的论述中会发现,调用一个函数是非常麻烦的事情,每次函数调用的时候,都要初始化这个函数的环境,而不是单纯的立刻执行函数中的过程代码,因此对于反复调用的复杂函数,可以考虑用闭包来实现优化,比如将功能函数写在闭包中,返回一个调用了功能函数的函数,这个返回函数调用了外层函数中的变量(就是这个功能函数),所以功能函数的初始化环境不会被释放,执行会非常快。

继承:利用原型链来继承,如cat想要继承animal,则cat.prototype = new animal();我们知道,在原型链的描述中,子类的prototype包含了父类的prototype的全部内容(同名属性覆盖),而new animal()是实例化一个对象,这个对象的__proto__指向了animal的prototype,这样的话cat的prototype的__proto__也指向了animal的prototype,完成了继承,但相应的constructor也变成了animal,所以要重新cat.prototype.constructor = cat(如果不修改的话,会对继承cat的子类早成错误影响)。(所以cat.prototype.__proto__ = animal.prototype应该也能完成继承,但两者有所不同,前者的prorotype除了使得__proto__指向animal的prototype以外,prototype本身等于了一个对象,也就是animal实例化出来的对象)。补充:通用而言,__proto__指向了其所属对象的构造函数的prototype,而对于一个函数对象,其__proto__指向了这个函数的构造函数的prototype,对于一个函数它的构造函数是谁呢?正是大Function。原型链的关系图如图所示:



类型检测
1、typeof
2、instanceof
3、Object.prototype.toString
4、constructor
5、duck type
1、typeof返回字符串,判断基本类型没有问题,NaN返回num,null返回object。在判断object类型时,function会单独返回function,但其他对象(如数组)返回object。
2、instanceof,做操作符必须是对象,不是对象直接返回false了(比如一个普通的字符串就是false但如果用new String就是true),右操作符必须是构造函数,判断规则是左边的对象的原型链上是否有右边构造函数的prototype属性,注意window的坑!!当页面内嵌iframe时,比如在iframe中有一个函数调用了instanceof(比如判断是否为Array),这个函数在父window中调用,参数是来自父window的一个数组,这样判断出来是false的!!!因为父窗口的数组原型链上没有iframe中Array构造函数的prototype,父窗口与iframe的Array构造函数各自为window的属性,window不一样,则Array不一样,此window下找到的Array自然不是彼window下的Array
3、Object.prototype.toString.call(),不能用Object.toString(),因为这个方法不定义在Object上面,再找Object的原型链为null了。不能用对象自己的toString,因为下级原型链上会有各自的toString方法。这个方法返回字符串,因而不会有跨window的问题。返回[object type],type只能是是内置的类型。但是null,undefined会返回object类型