首页 > 代码库 > 你应该知道的几个js知识
你应该知道的几个js知识
1.在javascript的块级作用域是以函数来决定的.
if(true){ var scopeName = "felayman"; } print(scopeName);上述虽然scopeName是在{}内声明的,但是它不属于块级作用域,在{}范围内依然能进行访问.因此,如果想在javascript中声明一个块级作用域,需要借助于函数来实现,如
<span style="font-size:18px;"> function scope(){ if(true){ var scopeName = "felayman"; } }</span>这样,scopeName才属于一个函数级(块级)作用域,在函数外是无法访问scopeName的.
2.javascript中的变量查找链,如
<span style="font-size:18px;"> var scope = "golbalScope"; var fun = function(){ alert(scopr); var scope = "funcScope"; }; fun();</span>其结果为undefined,上述的代码可能跟我们所想的有所不同,没有输出golbalScope,也没有输出funcScope,而是输出了undefined?这是声明原因呢?这个是javascript的一个重要特性---作用域搜索顺序,当我们去访问某个变量的时候,它会优先搜索scope所在环境,因此搜索到了var scope = "funcScope";,因此会
抛弃掉上一级环境的var scope = "golbalScope";也就是var scope = "golbalScope";会被变量scope所在的环境给屏蔽掉,然后当程序执行到alert(scope);的时候,但是
scope在当前环境中并没有进行初始化,因而输出了undefined.
3.javascript的函数作用域的嵌套
<span style="font-size:18px;"> var fun = function(){ var scope = "scope_one"; (function(){ alert(scope); var scope = "scope_two"; (function(){ alert(scope); })(); })(); }; fun();</span>
这个例子的结果为,undefined和scope_two,正如上面一个例子一样,我们可以理解其结果行为,但是我们必须去注意,函数作用域的嵌套关系是在定义的时候决定的,而不是调用的时候决定的,也就是说,javascript的作用域是静态作用域,也叫词法作用域,这个例子主要是想说明闭包,在javscript中,一般情况下闭包就是匿名函数,而关于闭包的定义,有这么一种说法:由函数及其封闭的自由变量组成的集合体.,因此在一般情况下,在javascript我们视函数即为闭包,我们看一个例子
<span style="font-size:18px;">function print(s){document.write(s+"<br/>")}; var getResult = function(){ var count = 0; var get = function(){ count++; return count; }; return get; }; var counter = getResult(); print(counter()); print(counter()); print(counter());</span>结果为1,2,3.如果熟悉其他语言,或许会恨困惑这个结果,因为函数,俗称方法,在其他语言中就是一个保存临时变量和结果的一个容器,当函数执行完毕后,会释放该函数内的所有的局部变量和临时结果,因此当我们每次调用同一个函数的时候,其结果是不变的,但是在jabascript,却因为闭包改变了这个规则.这个也是闭包的特性---当一个函数返回它内部定义的一个函数的时候,就产生了一个闭包,闭包不但包括被返回的函数,还包括这个函数的定义环境,上面的例子,当函数getResult()的内部函数get被一个外部变量counter引用的时候,counter和getResult()的局部变量就形成一个闭包.因此上述的counter()其实就是get(),打印counter()的值,就是打印get()的值,但是于此同时get()函数属于getResult()函数内部的一个闭包(或者可以理解一个布局变量,或者理解get()在getResult()函数的作用域内),在该闭包内依然引用着它的上一级作用域,即getResult()函数作用域,具体的说,在get()函数内部使用了getResult()函数内的变量,因此,虽然我们在getResult()函数外部调用了一个看似与getResult()函数关系不大的counter()函数,但是却是在getResult()函数内部环境去执行的counter()函数.也就是说print(counter());
print(counter());print(counter());)这段代码看似是在getResult()外部执行,其实本质上是在getResult()函数内部执行的,因此上述代码完全等效于下述代码:
<span style="font-size:18px;">/** * Created by felayman on 2014/5/27. */ function print(s){document.write(s+"<br/>")}; var getResult = function(){ var count = 0; var get = function(){ count++; return count; }; print(get()); print(get()); print(get()); }; getResult()</span>;但是如果我们在使用的时候这样使用的话,就没有闭包的优势了----与外界失去了联系,也就是与其他语言中的函数(方法)一样的,而javascript中的函数,也正是由于闭包的特性,才被称为最实用于函数式编程的语言.
4.this所指向的内容
function print(s){document.write(s+"<br/>")} var func1 = { name:"func1scope", func:function(){ print(this.name); } }; var func2 = { name:"func2scope" }; func1.func(); //输出func1 func2.func = func1.func; func2.func(); //输出func2 var name = "windowscopr"; window.func = func1.func; func();
上述代码会输出:func1scope func2scope windowscopr,因此,可以看出this的指向是随着其环境而发生变化,但是我们只需要记着一句话即可---this始终指向其执行期间的环境对象,而非定义时的环境对象.
5.call和apply
如果说在javascript中最让人困惑和难以理解的,
其中call和apply的功能是以不同的对象作为上下文来调用某个对象的函数,或者--就是允许在一个对象中去调用另一个对象中的同名函数(没有会被复制)并执行(摘自--NodeJS开发指南).如下面的例子:
<span style="font-size:18px;">var oneUser = { name:"oneUser", getName:function(){ alert(this.name); } }; var otherUser = { name:"otherUser", }; oneUser.getName.call(otherUser);</span>可以这样理解,我们以每一个对象内部都有一个call对象(函数就是对象),该对象没有具体指向,但是允许指向任何一个对象内部的另一个对象.而applay与call的用法没有上面区别,只是接受的参数不同而已.
6.原型
如果要给原型一个定义,百度百科上说的很详细,但是很抽象,很难理解,而在NodeJs开发指南中,作者用了一个很好的例子----原型就好像是一件艺术品的原件,我们通过一台100%精确的机器把这个原件复制出很多件,如果我们了解类似java的面向对象语言的话,就会知道,在java中所有的对象都来自于类,而C++中所有的对象来自于构造器,但是javascript是一种基于面向对象语言,它既有类似C++的构造器,也有类似类的原型.其实我更倾向于一篇博文中的理解,在javascript中
,原型就是java中的一个类的一个对象,该对象存储该类的所有静态属性或方法.我更建议使用这种方法来理解原型.因为的确是这样的,javascript中使用原型的目的就是让某一类型的所有对象共享一些属性和函数,提供代码的复用性,同时也隐藏一些属性和函数的细节,即提高其内聚性和封装性,这也是javascript基于面向对象的特性.
<span style="font-size:18px;">function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.getInfo = function(){ return "name ="+this.name+",age = "+ this.age+",job = " +this.job; } } </span>
上述代码没有使用,原型,因此我们在使用new关键字来创建对象的时候,可以根据不同的name,age,job来构件许许多多的Person的对象,这些对象的属性都不相同,但是作为常识,我们都知道Person都有一个共同的行为--说话(不去钻牛角尖),当然我们可以改写上面的代码来满足我们的需求:
<span style="font-size:18px;">function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.getInfo = function(){ return "name ="+this.name+",age = "+ this.age+",job = " +this.job; } this.say = function(){ alert("人都会说话"); } } </span>
这样的代码是允许的,但是不是被建议的,因为我们在创建一个Person对象的时候,就会在内存中生成say()函数的内容空间,或许在少量的这样的行为的时候我们允许这么做,但是我们再思考一下,如果一个函数中有大量的行为或者属性都像say()函数一样是每一个Person对象所共同拥有的,必然Person会说话,会跑,会思考,会苦,会笑.........如果有了大量类似的属性或行为的话,我们还是写在函数内部的话,就不好了,因为我们在创建基量很大的对象数量的时候,我们就额外增加了非必须且可避免的内存开销了,因为每个对象在内存中都会开辟其所拥有的方法空间,这是不建议的,因此,我们使用了原型的话,这个问题就可以避免了,我们只需要在内存的其他地方创建一个原型对象,改对象保存着Person所有的共同属性或者方法,只要在每个Person实例对象中保存一个指向改内存空间的引用或句柄即可.这样当我们即使创建再多Person实例对象,也没有多少内存开销,这个理解就是javascript原型的特性之一.因此上述代码改写成下面就好了:
<span style="font-size:18px;">function Person(name,age,job){ this.name = name; this.age = age; this.job = job; } Person.prototype.getInfo = function(){ return "name = "+this.name+",age = "+this.age+",job ="+this.job; }; Person.prototype.say =function(){ console.log("人都会说话"); };</span>