JQuery插件模板

//You need an anonymous function to wrap around your function to avoid conflict

(function($){

    //Attach this new method to jQuery

  $.fn.extend({

   //This is where you write your plugin's name

   pluginname: function() {

    //options

     var defaults = {

     option1: "default_value"

     }

     var options = $.extend(defaults, options);

     //a public method

     this.methodName: function () {

        //call this method via $.pluginname().methodName();

     }

//Iterate over the current set of matched elements

     return this.each(function() {

       var o = options;

      //code to be inserted here 

      });

     }

    }); 

//pass jQuery to the function,

//So that we will able to use any valid Javascript variable name

//to replace "$" SIGN. But, we'll stick to $ (I like dollar sign: ) )     

})(jQuery);

 

js对象的使用

公司最近在招web fronter,免不了需要进行面试,于是大家在群里讨论一些题目。其中一题如下:

Js代码  

var a = new Object;  

var b = new Object;  

var c = new Object;  

c[a]=a;  

c[b]=b;  

alert(c[a]===a); //输出什么  

 

这个题目还是很有意思的,第一反应往往会心中涌起一丝疑惑,这难道不是true么(我就是的= =!)事实上还真输出了一个false...这样也许看不出什么端倪,但是如果稍微变动一下代码:

Js代码  

var a = new Object;  

var b = new Object;  

var c = new Object;  

c[a]=a;  

c[b]=b;  

alert(c[a]===b); //输出true  

alert(c[b]===b); //输出true  

 

从结果可以明显的推断出一个事实:当运行了c[b]=b这条语句之后,原先的属性c[a]被覆盖了。也就是说,其实c[a]和c[b]指向的是同一个东西。

 

创建JS对象

现在需要深究一下,JS对象的属性究竟是怎么回事...一般而言,我们会认为JS中的对象就是一个无序的属性集,这个属性集中的每个属性都由键值对构成,就好比一种散列表类似的数据结构。对象里属性的值可以用来存放JS中原型的值、对象的引用、函数的引用。这在Ecmascript-262标准中可以找到理论依据:

Ecmascript-262 3rd

An object is a member of the type Object. It is an unordered collection of properties each of which
contains a primitive value, object, or function. A function stored in a property of an object is called a method.

 

JS中有两种创建对象的方式,一种是通过new运算符,还有一种是通过字面量的方式。前文的面试题中就是利用了new Object来创建一个貌似是空的对象。下面来分别分析一下这两种创建对象的方式:

 

1.字面量

字面量创建的方式很简单,根据Ecma标准语法:

Ecma 262 10.1.5

ObjectLiteral :
     { }
     { PropertyNameAndValueList }
PropertyNameAndValueList :
     PropertyName : AssignmentExpression
     PropertyNameAndValueList , PropertyName : AssignmentExpression
PropertyName :
     Identifier
     StringLiteral
     NumericLiteral

根据描述,如果创建的不是空对象,而是一个带有Name和Value的对象,那么Name可以是JS中的标识符、字符串或者数字。具体的创建过程是可以描述成:

1)var obj = {} 就等于var obj = new Object()

The production ObjectLiteral : {} is evaluated as follows:
1. Create a new object as if by the expression new Object().
2. Return Result(1).

 

2)var obj = { PropertyNameAndValueList }

如果是这种带了键值对的对象,首先还是调用new Object()来创建一个新的对象,然后会计算PropertyName、AssignmentExpression,利用GetValue方法获取AssignmentExpression的值,最后调用被新创建对象的[[Put]] 方法(obj的put方法是内部方法,外部无法调用),具体细节如下:

The production ObjectLiteral : { PropertyNameAndValueList } is evaluated as follows:
1. Evaluate PropertyNameAndValueList.
2. Return Result(1);
The production PropertyNameAndValueList : PropertyName : AssignmentExpression is evaluated as follows:
1. Create a new object as if by the expression new Object().
2. Evaluate PropertyName.
3. Evaluate AssignmentExpression.
4. Call GetValue(Result(3)).
5. Call the [[Put]] method of Result(1) with arguments Result(2) and Result(4).
6. Return Result(1).

这里的GetValue和[[Put]]方法都可以暂且不管,因为它们对于程序员并不可见。进一步看一下Evaluate PropertyName的过程:

The production PropertyName : Identifier is evaluated as follows:
1. Form a string literal containing the same sequence of characters as the Identifier.
2. Return Result(1).

The production PropertyName : StringLiteral is evaluated as follows:
1. Return the value of the StringLiteral.

The production PropertyName : NumericLiteral is evaluated as follows:
1. Form the value of the NumericLiteral.
2. Return ToString(Result(1)).

可以发现,在利用字面量创建对象的时候:如果属性的nameJS中的标识符表示,那么name会被转成值相同的字符串;如果属性的namenumber,那么会调用ToString来计算该number的字符串表示,这儿的ToString也是JS内部的方法。

 

2.利用new Object()

调用new Object()也是一种创建对象的方法,如果不传参数进去,本质上来说跟直接使用字面量{}没有区别。实际上new Object的过程还是有些复杂的:

new Object ( [ value ] ) 
When the Object constructor is called with no arguments or with one argument value, the following steps are taken:
1. If value is not supplied, go to step 8.
2. Ifthetypeof value isnotObject,gotostep5.
3. If the value is a native ECMAScript object, do not create a new object but simply return value.
4. If the value is a host object, then actions are taken and a result is returned in an implementation-dependent manner that may depend on the host object.
5. Ifthetypeof value is String, return ToObject(value).
6. Ifthetypeof value is Boolean, return ToObject(value).
7. Ifthetypeof value is Number, return ToObject(value).
8. (The argument value was not supplied or its type was Null or Undefined.)Create a new native ECMAScript object.
The [[Prototype]] property of the newly constructed object is set to the Object prototype object.
The [[Class]] property of the newly constructed object is set to "Object".
The newly constructed object has no [[Value]] property.
Return the newly created native object

很显然,如果是不传参数,那么会创建一个 native ECMAScript object,随后会给这个object添加一系列的内部属性 ,比如将 [[Prototype]]设置为Object prototype object(即Object.prototype),将[[Class]]设置为字符串“Object”。注意,在Object.prototype中已经包含了一些方法:

        1.toString ( )

        2.toLocaleString ( )

        3.valueOf ( )

        4.hasOwnProperty (V)

        5.isPrototypeOf (V)

        6.propertyIsEnumerable (V)

利用new Object新创建的对象不会有除了上面之外别的方法。

 

对象的属性访问、赋值

对JS中的对象进行属性访问同样也是两种形式,一种是利用“[ ]”运算符,还有一种是利用“.”运算符。即:

CallExpression. Identifier 或CallExpression[ Expression ]

注意就是利用“.”的时候,只能后接一个合法的Identifie。但是如果是利用“[ ]”却可以包含一个表达式进来,本文刚开始的题目中就是利用这种形式去访问obj的属性。其实CallExpression. Identifier也会按照CallExpression[ <Identifier-string>]去执行。

CallExpression[ Expression ] is evaluated as follows:
1. Evaluate CallExpression.
2. Call GetValue(Result(1)).
3. Evaluate Expression.
4. Call GetValue(Result(3)).
5. Call ToObject(Result(2)).
6. Call ToString(Result(4)). 
7. Return a value of type Reference whose base object is Result(5) and whose property name is Result(6).

尤其要注意第6行, 所有方括号中的Expression的值要经过ToString这一步。

 

对于赋值,具体的计算过程如下:

The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:
1. Evaluate LeftHandSideExpression.
2. Evaluate AssignmentExpression.
3. Call GetValue(Result(2)).
4. Call PutValue(Result(1), Result(3)).

CallExpression就是一种 LeftHandSideExpression。

 

题目详解:

下面来拆分题目,逐行来推敲具体的执行过程,首先是三条创建对象的语句:

Js代码  

//创建一个native Ecmascript object  

//[[Prototype]]指向Object.prototype  

//[[Class]]设为"Object"  

var a = new Object;  

//同a  

var b = new Object;  

//同a  

var c = new Object;  

注意,一个obj里并非只有 [[Prototype]]和[[Class]]两个内部属性,具体的内部属性以及内部方法可以参考Ecma262的8.6章节。

 

在创建完对象后,又是两条属性赋值语句。

Js代码  

//c[a]会按照CallExpression[ Expression ] 的7个步骤来计算,  

//其中的GetValue较为复杂,可以参考http://www.w3help.org/zh-cn/causes/SD9028的注释2  

//注意第6步,ToString(a)会调用到ToPrimitive(a),进而调用a的[[DefaultValue]]方法,具体参考Ecma规范  

//这里最终会调用到a.toString方法,根据Object.prototype.toString的描述,会返回[object Object]  

//即最终相当于c["[object Object]"]=a;  

c[a]=a;  

//即最终相当于c["[object Object]"]=b;  

c[b]=b;  

alert(c[a]===a);  

 

稍微变个形

Js代码  

var a = {};  

var b = {};  

var c = {};  

c.a=a;  

c.b=b;  

alert(c[a]===a);  

你可能会立马想到,c.a是怎么处理的,很显然这是利用了“.”运算符访问属性,而不是“[ ]”。根据上面的描述, CallExpression. Identifier会按照CallExpression[ <Identifier-string>]去执行,那么这里的c.a就相当于c["a"]。因此最后结果肯定是true了?

 

真的是这样么?别忘了,在alert语句中还有c[a]的存在,如前文所述,c[a]完全就相当于c["[object Object]"],因此这段代码相当于:

Js代码  

var a = {};  

var b = {};  

var c = {};  

c["a"]=a;  

c["b"]=b;  

alert(c["[object Object]"]===a);  

真是相当的恶心....

 

再来变一变

这次c的属性直接在声明里写好了。

Js代码  

var a = {};  

var b = {};  

var c = {  

    a:a,  

    b:b  

};  

alert(c.a===a);  

这回是利用了字面量创建对象的方式。根据上面的描述,如果键值对的name是一个合法的JS标识符,那么name就是将该标识符变成直接字符串,简单来说,就是两边加上引号。

因此,上面这段代码相当于:

Js代码  

var a = {};  

var b = {};  

var c = {};  

c["a"]=a;  

c["b"]=b;  

alert(c.a===a);  

终于输出了true....

 

原型链继承

Call和Apply继承

Call 父类里的this属性相当于public,var相当与private,new出的对象调用this方法时,var声明的变量还在,只不过外部不可见。当类的函数用到公共函数时,注意这里的公共函数里的this就会改变。当公共函数返回到调用函数时,继续执行时this又回到原来调用对象。用在类里设置var that = this;记录下来this对象就可以使用对象的属性了。当js文件加载时会把所有的变量和最外层的函数名存入自己的库,函数内部不知道是什么,当嵌套函数里使用this时,要注意this的指向。比如

TEvent.addListener(this._map,"zoom-end",function(){
     that.zoomInOrOut(that._markers,that._historyData,that._labelmarkers,showLabelText);
});

当执行到外层时this还是当前对象。当内嵌函数在执行的时候this就已经变了,里面的this和外面的this不是一个对象。

 <script type="text/javascript"> 

  

   function Parent(username){

    this.name = [5];

       var bd = 34;

       var that = this;

       var dd = 99;

       this.pushn = function(d){

         //this.pushn1(this.bd+d);

         this.innerf(d);

         ffff(that,dd);

       }

       this.pushn12 = function(d){

         //this.pushn1(this.bd+d);

         this.innerf(d);

       }

       function ffff(that,dd){

        

         that.pushn12(dd);

       }

  }

  function Child(username){

    Parent.call(this, username);

    

   this.innerf = function(d){

              alert(this.bd);

        this.name.push(d);

    }

    //this.pushn1 = function(d){

       //     alert(this.bd);

    //    this.name.push(d);

    //}

  }

 function Child1(username){

    Parent.call(this, username);

    var aaa = 222;

    

    this.pushn1 = function(d){

               this.asd();

        this.name.push(d);

              this.name.push(d);

    }

       this.asd = function(){

              alert(444);

       }

       this.getaaa = function(){

              return aaa;

       }

  }

  var child1 = new Child("lisi");

  var child2 = new Child("ddd");

  alert(child1.name);

  alert(child2.name);

  try{

  child1.pushn(11); }catch(e){alert(e);}

  child2.pushn12(21);

  alert(child1.name);

  alert(child2.name);

  var child3 = new Child1("ddd");

  //child3.pushn(11);

  alert(child3.getaaa());

</script>

动态链接

在写动态网页连接时,连接会当做字符串存储,这样连接里就不能装入对象类型。这里只能传递数字或字符。

This作用域

var someuser = {

    name: 'byvoid',

    func: function() {

        console.log(this.name);

    }

};

var foo = {

    name: 'foobar'

};

someuser.func(); // 输出 byvoid

foo.func = someuser.func;

foo.func(); // 输出 foobar

name = 'global';

func = someuser.func;

func(); // 输出 global

在 JavaScript 中,上下文对象就是 this 指针,即被调用函数所处的环境。上下文对象 的作用是在一个函数内部引用调用它的对象本身。在 JavaScript 中,本质上,函数类型的变量是指向这个函数实体的一个引用,在引用之 间赋值不会对对象产生复制行为。我们可以通过函数的任何一个引用调用这个函数,不同之 处仅仅在于上下文。仔细观察上面的例子,使用不同的引用来调用同一个函数时,this 指针永远是这个引用所属的对象

var scope = 'top';

var f1 = function() {

    console.log(scope);

};

f1(); // 输出 top

var f2 = function() {

    var scope = 'f2';

    f1();

};

f2(); // 输出 top

 函数作用域的嵌套关系是定义时决定的,而不是调用时决定的,也就 是说,JavaScript 的作用域是静态作用域,又叫词法作用域,这是因为作用域的嵌套关系可 以在语法分析时确定,而不必等到运行时确定

这两段的代码注意等号右边的对象和函数的定义形式。

jQuery动态创建html元素

一般来说,可以通过以下几种方式动态创建html元素:

1、使用jQuery创建元素的语法

2、把动态内容存放到数组中,再遍历数组动态创建html元素

3、使用模版

1.使用jQuery动态创建元素追加到jQuery对象上。

  <script type="text/javascript">

    $(function() {

      $('<input />', {

        id: 'cbx',

        name: 'cbx',

        type: 'checkbox',

        checked: 'checked',

        click: function() {

          alert("点我了~~");

        }

      }).appendTo($('#wrap'));

    });

  </script>

  <div id="wrap"></div>

2.先把内容放到数组中,然后遍历数组拼接成html

  <script src="Scripts/jquery-1.10.2.js"></script>

  <style type="text/css">

    table {

      border: solid 1px red;

      border-collapse: collapse;

    }

 

    td {

      border: solid 1px red;

    }

  </style>

  <script type="text/javascript">

    $(function () {

      var data = ["a", "b", "c", "d"];

      var html = '';

      for (var i = 0; i < data.length; i ++) {

        html += "<td>" + data[i] + "</td>";

      }

      $("#row").append(html);

    });

  </script>

  <table>

    <tr id="row"></tr>

  </table>

3.使用模版生成html

   <script type="text/javascript">

    $(function () {

      var a = buildHTML("a", "我是由模版生成的", {

        id: "myLink",

        href: "http://www.baidu.com"

      });

      $('#wrap1').append(a);

      var input = buildHTML("input", {

        id: "myInput",

        type: "text",

        value: "我也是由模版生成的~~"

      });

      $('#wrap2').append(input);

    });

    buildHTML = function(tag, html, attrs) {

      // you can skip html param

      if (typeof (html) != 'string') {

        attrs = html;

        html = null;

      }

      var h = '<' + tag;

      for (attr in attrs) {

        if (attrs[attr] === false) continue;

        h += ' ' + attr + '="' + attrs[attr] + '"';

      }

      return h += html ? ">" + html + "</" + tag + ">" : "/>";

    };

  </script>

  <div id="wrap1"></div>

  <div id="wrap2"></div>

<style type="text/css">

.warpper{ border:1px solid red; padding:8px;}

</style>

<script type="text/javascript" language="javascript" src="JavaScript/jquery-1.6.1.min.js" ></script>

<script type="text/javascript" language="javascript">

<!--

///动态创建一个div

$(function(){

$('<div />',{

id:'test',

text:"this is a div",

"class":"warpper",

click:function(){

var text=$(this).text();

alert(text);

}

}).appendTo("body");

});

//创建input:text

$(function(){

$('<input />',{

type:"text",

val:"input text somethings...",

name:"userName"

}).appendTo("body");

});

//创建input select

$(function(){

var slt=$('<select />',{name:"country" });

$('<option />',{

val:"0",

text:"请选择"

}).appendTo(slt);

$('<option>',{

val:"CN",

text:"China",

selected:"selected"

}).appendTo(slt);

$("body").append(slt);

});

//创建radio

$(function(){

$('<input />',{

type:"radio",

name:"rdo",

checked:"checked",

val:"男"

}).appendTo("body");

$('<label>',{

text:"男",

}).appendTo("body");

$('<input />',{

type:"radio",

name:"rdo",

val:"女"

}).appendTo("body");

$('<label>',{

text:"女"

}).appendTo("body");

});

//creat checkbox

$(function(){

$('<input />',{

type:"checkbox",

name:"cbox",

val:"1",

checked:"checked"

}).appendTo("body");

});

$(function(){

$('<input />',{

type:"file",

name:"myfile"

}).appendTo("body");

});

//-->

</script>

</head>

</body>

</html>

(function($){...})(jQuery) 匿名函数

function(arg){...}这就定义了一个匿名函数,参数为arg。而调用函数时,是在函数后面写上括号和实参的,由于操作符的优先级,函数本身也需要用括号,即:(function(arg){...})(param)。这就相当于定义了一个参数为arg的匿名函数,并且将param作为参数来调用这个匿名函数。而(function($){...}) (jQuery)则是一样的,之所以只在形参使用$,是为了不与其他库冲突,所以实参用jQuery

其实就等于:var fn = function($){....}; fn(jQuery);其实可以这么理解,不过要注意的是fn是不存在的。那个函数直接定义,然后就运行了。就“压缩”成下面的样子了(function($){...})(jQuery)

简单理解是(function($){...})(jQuery)用来定义一些需要预先定义好的函数

$(function(){ })则是用来在DOM加载完成之后运行\执行那些预行定义好的函数.

开发jQuery插件时总结的一些经验分享一下。

一、先看

jQuery(function(){ });

全写为

jQuery(document).ready(function(){ });

意义为在DOM加载完毕后执行了ready()方法。

二、再看

(function(){ })(jQuery);

其实际上是执行()(para)匿名方法,只不过是传递了jQuery对象。

三、总结

jQuery(function(){ });用于存放操作DOM对象的代码,执行其中代码时DOM对象已存在。不可用于存放开发插件的代码,因为jQuery对象没有得到传递,外部通过jQuery.method也调用不了其中的方法(函数)。

(function(){ })(jQuery);用于存放开发插件的代码,执行其中代码时DOM不一定存在,所以直接自动执行DOM操作的代码 请小心使用。

jQuery.extend

Jquery的扩展方法extend是我们在写插件的过程中常用的方法,该方法有一些重载原型,在此,我们一起去了解了解。

  一、Jquery的扩展方法原型是: extend(dest,src1,src2,src3...);

  它的含义是将src1,src2,src3...合并到dest中,返回值为合并后的dest,由此可以看出该方法合并后,是修改了dest的结构的。如果想要得到合并的结果却又不想修改dest的结构,可以如下使用:

  var newSrc=$.extend({},src1,src2,src3...)//也就是将"{}"作为dest参数。这样就可以将src1,src2,src3...进行合并,然后将合并结果返回给newSrc了。如下例:var result=$.extend({},{name:"Tom",age:21},{name:"Jerry",sex:"Boy"})

那么合并后的结果  result={name:"Jerry",age:21,sex:"Boy"} 也就是说后面的参数如果和前面的参数存在相同的名称,那么后面的会覆盖前面的参数值。

 二、省略dest参数

  上述的extend方法原型中的dest参数是可以省略的,如果省略了,则该方法就只能有一个src参数,而且是将该src合并到调用extend方法的对象中去,如:

   1、$.extend(src)

   该方法就是将src合并到jquery的全局对象中去,如:

 $.extend({

  hello:function(){alert('hello');}

  });

就是将hello方法合并到jquery的全局对象中。

   2、$.fn.extend(src)

   该方法将src合并到jquery的实例对象中去,如:

 $.fn.extend({

  hello:function(){alert('hello');}

 });

 就是将hello方法合并到jquery的实例对象中。

下面例举几个常用的扩展实例:

$.extend({net:{}});

   这是在jquery全局对象中扩展一个net命名空间。

  $.extend($.net,{

   hello:function(){alert('hello');}

  })

   这是将hello方法扩展到之前扩展的Jquery的net命名空间中去。

三、Jquery的extend方法还有一个重载原型: 

extend(boolean,dest,src1,src2,src3...) 第一个参数boolean代表是否进行深度拷贝,其余参数和前面介绍的一致,什么叫深层拷贝,我们看一个例子:

var result=$.extend( true,  {}, 

    { name: "John", location: {city: "Boston",county:"USA"} }, 

    { last: "Resig", location: {state: "MA",county:"China"} } );

  我们可以看出src1中嵌套子对象location:{city:"Boston"},src2中也嵌套子对象location:{state:"MA"},第一个深度拷贝参数为true,那么合并后的结果就是:

result={name:"John",last:"Resig",

location:{city:"Boston",state:"MA",county:"China"}}

也就是说它会将src中的嵌套子对象也进行合并,而如果第一个参数boolean为false,我们看看合并的结果是什么,如下:

var result=$.extend( false, {}, 

{ name: "John", location:{city: "Boston",county:"USA"} }, 

{ last: "Resig", location: {state: "MA",county:"China"} }  );

那么合并后的结果就是:  resul={name:"John",last:"Resig",location:{state:"MA",county:"China"}}

JS之匿名函数与闭包

var a = function(t) { return t; } (1 + 2).toString(); 

alert(a);  

这里就是定义了一个匿名函数,传入参数(1+2)并且执行,然后赋值给变量a。因为在第一行没有写分号.闭包通常来讲可以简单的认为是可以访问一个函数里面的局部变量的另外一个函数。在下面的例子中,prop是在函数test里面定义的局部变量,正常调用完成之后不能再被访问,通过下面的方式可以在test函数执行完之后在后续的某个时间点继续访问test函数定义的局部变量,这就是闭包的一个简单示例。局部变量正常应该在方法执行完之后释放,但是这里的局部变量驻留在内存中。

function test(){ 

    var prop = 1; 

    return function(){ 

        return prop++; 

    }

}

var t = test();//outputs 1,因为prop++没有执行 

alert(t()); 

alert(t()); 

以匿名函数的方式执行一个初始化,同时有起到隔离作用域的效果

(function(){ 

alert('anonymous'); 

})();  

//同时传参数的例子 

(function(v){ 

    alert('anonymous ' + v); 

})('test');

闭包函数体在外部函数执行的时候是不执行的:下面这个例子是比较好理解的。

function test(){ 

    var prop = 1; 

   f = function(){ 

        return prop++; 

    } 

   alert(prop); 

var t = test(); 

下面的例子呢?alert(fa[0]);输出的是函数内容,{return i},可以看到函数体本身没有被替换成0,1,2...

function test(){ 

    var arr = []; 

    for(var i =0; i< 5; i++){ 

        arr[i] = function(){ 

            return i; 

        } 

    }     

    return arr;      

var fa = test(); 

alert(fa[0]); 

alert(fa[0]()); //outputs is 5 ??? 

//定义及时执行, 

    for(var i =0; i< 5; i++){ 

        arr[i] = (function(num){ 

            return num; 

        })(i);//匿名函数同时也执行了 

    } 

//定义及时执行,返回的是函数 

    for(var i =0; i< 5; i++){ 

        //var ri = i; 

        arr[i] = (function(num){ 

            return function(){  

                return num; 

            } 

        })(i); 

    } 

闭包里的this指向的是window

function test(){ 

    alert(this); 

  return function(){ 

       alert(this); 

   } 

var t = new test(); //alert object 

t(); //alert window 

//解决办法: 

    var that = this; 

  return function(){ 

       alert(that); 

   } 

闭包引起的内存泄漏,浏览器关闭才释放内存。(怎么验证??)

</head>   

<body>   

    <span id='sid'>textspan</span> 

    <script type="text/javascript">   

    function test(){ 

        var ospan = document.getElementById('sid'); 

        ospan.onclick = function(){ 

            alert(ospan.innerHTML); 

        } 

    } 

    test();  

    </script>   

</body>   

</html> 

匿名函数自执行时定义对象,定义的对象用到了匿名函数的局部变量,但是对于这个对象来说,这个变量却是全局的。

(function(){ 

    var v = 't'; 

    Test = function(val){ 

        v = val;//通过这种方式在每次 new Test的时候改变v

        this.myVal = val;

        this.getVal = function(){

            return v; 

        }

    }; 

})(); 

var oTest = new Test('value1'); 

alert(oTest.getVal()); 

var oTest2 = new Test('value2'); 

alert(oTest.getVal()); //对象oTest 值变成了value2

javascript 闭包

一、变量的作用域

要理解闭包,首先必须理解Javascript特殊的变量作用域。

变量的作用域无非就是两种:全局变量和局部变量。

Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。

Js代码

  var n=999;

  function f1(){

    alert(n);

  }

  f1(); // 999

另一方面,在函数外部自然无法读取函数内的局部变量。

Js代码

  function f1(){

    var n=999;

  }

  alert(n); // error

这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

Js代码

  function f1(){

    n=999;

  }

  f1();

  alert(n); // 999

--------------------------------------------------------------------------------------------------------

二、如何从外部读取局部变量?

出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。

那就是在函数的内部,再定义一个函数。

Js代码

  function f1(){

    n=999;

    function f2(){

      alert(n); // 999

    }

  }

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1 就是不可见的。这就是Javascript语言特有的“链式作用域”结构(chain scope),

子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

 

Js代码

  function f1(){

    n=999;

    function f2(){

      alert(n);

    }

    return f2;

  }

  var result=f1();

  result(); // 999

--------------------------------------------------------------------------------------------------------

三、闭包的概念

上一节代码中的f2函数,就是闭包。

各种专业文献上的“闭包”(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。

由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

--------------------------------------------------------------------------------------------------------b

四、闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

怎么来理解这句话呢?请看下面的代码。

 

Js代码

  function f1(){

    var n=999;

    nAdd=function(){n+=1}

    function f2(){

      alert(n);

    }

    return f2;

  }

  var result=f1();

  result(); // 999

  nAdd();

  result(); // 1000

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此 nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个

匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

--------------------------------------------------------------------------------------------------------

五、使用闭包的注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便

改变父函数内部变量的值。

--------------------------------------------------------------------------------------------------------

六、思考题

如果你能理解下面代码的运行结果,应该就算理解闭包的运行机制了。

Js代码

  var name = "The Window";  

  var object = {  

    name : "My Object",  

    getNameFunc : function(){  

      return function(){  

        return this.name;  

     };  

    }  

};  

alert(object.getNameFunc()());  //The Window

--------------------------------------------------------------------------------------------------------

JavaScript闭包例子

function outerFun()

 {

  var a=0;

  function innerFun()

  {

   a++;

   alert(a);

  }   

 }

innerFun()

上面的代码是错误的.innerFun()的作用域在outerFun()内部,所在outerFun()外部调用它是错误的.

改成如下,也就是闭包:

Js代码

function outerFun()

{

 var a=0;

 function innerFun()

 {

  a++;

  alert(a);

 }

 return innerFun;  //注意这里

}

var obj=outerFun();

obj();  //结果为1

obj();  //结果为2

var obj2=outerFun();

obj2();  //结果为1

obj2();  //结果为2

什么是闭包:

当内部函数 在定义它的作用域 的外部 被引用时,就创建了该内部函数的闭包 ,如果内部函数引用了位于外部函数的变量,当外部函数调用完毕后,这些变量在内存不会被 释放,因为闭包需要它们.

--------------------------------------------------------------------------------------------------------

再来看一个例子

Js代码

function outerFun()

{

 var a =0;

 alert(a); 

}

var a=4;

outerFun();

alert(a);

结果是 0,4 .  因为在函数内部使用了var关键字 维护a的作用域在outFun()内部.

再看下面的代码:

Js代码

 

function outerFun()

{

 //没有var

 a =0;

 alert(a); 

}

var a=4;

outerFun();

alert(a);

结果为 0,0 真是奇怪,为什么呢?

作用域链是描述一种路径的术语,沿着该路径可以确定变量的值 .当执行a=0时,因为没有使用var关键字,因此赋值操作会沿着作用域链到var a=4;  并改变其值.

--------------------------------------------------------------------------------------------------------------------------------------------------

如果你对javascript闭包还不是很理解,那么请看下面转载的文章:(转载:http://www.felixwoo.com/archives/247)

 

一、什么是闭包?

官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

相信很少有人能直接看懂这句话,因为他描述的太学术。其实这句话通俗的来说就是:JavaScript中所有的function都是一个闭包。不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的“闭包”。看下面这段代码:

function a() {

 var i = 0;

 function b() { alert(++i); }

 return b;

}

var c = a();

c();

这段代码有两个特点:

1、函数b嵌套在函数a内部;

2、函数a返回函数b。

引用关系如图:

 

  这样在执行完var c=a()后,变量c实际上是指向了函数b,再执行c()后就会弹出一个窗口显示i的值(第一次为1)。这段代码其实就创建了一个闭包,为什么?因为函数a外的变量c引用了函数a内的函数b,就是说:

  当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。

  让我们说的更透彻一些。所谓“闭包”,就是在构造函数体内定义另外的函数作为目标对象的方法函数,而这个对象的方法函数反过来引用外层函数体中的临时变量。这使得只要目标 对象在生存期内始终能保持其方法,就能间接保持原构造函数体当时用到的临时变量值。尽管最开始的构造函数调用已经结束,临时变量的名称也都消失了,但在目 标对象的方法内却始终能引用到该变量的值,而且该值只能通这种方法来访问。即使再次调用相同的构造函数,但只会生成新对象和方法,新的临时变量只是对应新 的值,和上次那次调用的是各自独立的。

二、闭包有什么作用?

  简而言之,闭包的作用就是在a执行完并返回后,闭包使得Javascript的垃圾回收机制GC不会收回a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量。这是对闭包作用的非常直白的描述,不专业也不严谨,但大概意思就是这样,理解闭包需要循序渐进的过程。

在上面的例子中,由于闭包的存在使得函数a返回后,a中的i始终存在,这样每次执行c(),i都是自加1后alert出i的值。

  那 么我们来想象另一种情况,如果a返回的不是函数b,情况就完全不同了。因为a执行完后,b没有被返回给a的外界,只是被a所引用,而此时a也只会被b引 用,因此函数a和b互相引用但又不被外界打扰(被外界引用),函数a和b就会被GC回收。(关于Javascript的垃圾回收机制将在后面详细介绍)

三、闭包内的微观世界

  如果要更加深入的了解闭包以及函数a和嵌套函数b的关系,我们需要引入另外几个概念:函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。以函数a从定义到执行的过程为例阐述这几个概念。

当定义函数a的时候,js解释器会将函数a的作用域链(scope chain)设置为定义a时a所在的“环境”,如果a是一个全局函数,则scope chain中只有window对象。

当执行函数a的时候,a会进入相应的执行环境(excution context)。

在创建执行环境的过程中,首先会为a添加一个scope属性,即a的作用域,其值就为第1步中的scope chain。即a.scope=a的作用域链。

然后执行环境会创建一个活动对象(call object)。活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JavaScript代码直接访问。创建完活动对象后,把活动对象添加到a的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象。

下一步是在活动对象上添加一个arguments属性,它保存着调用函数a时所传递的参数。

最后把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。

到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c,又函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。

当函数b执行的时候亦会像以上步骤一样。因此,执行时b的作用域链包含了3个对象:b的活动对象、a的活动对象和window对象,如下图所示:

 

如图所示,当在函数b中访问一个变量的时候,搜索顺序是:

先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。

如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。

如果整个作用域链上都无法找到,则返回undefined。

小结,本段中提到了两个重要的词语:函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:

function f(x) {

  var g = function () { return x; }

  return g;

}

var h = f(1);

alert(h());

这段代码中变量h指向了f中的那个匿名函数(由g返回)。

假设函数h的作用域是在执行alert(h())确定的,那么此时h的作用域链是:h的活动对象->alert的活动对象->window对象。

假设函数h的作用域是在定义时确定的,就是说h指向的那个匿名函数在定义的时候就已经确定了作用域。那么在执行的时候,h的作用域链为:h的活动对象->f的活动对象->window对象。

如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。

运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了。

 

四、闭包的应用场景

保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。

在内存中维持一个变量。依然如前例,由于闭包,函数a中i的一直存在于内存中,因此每次执行c(),都会给i自加1。

通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)

私有属性和方法在Constructor外是无法被访问的

function Constructor(...) { 

  var that = this; 

  var membername = value;

  function membername(...) {...}

}

以上3点是闭包最基本的应用场景,很多经典案例都源于此。

 

五、Javascript的垃圾回收机制

在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。

 

六、结语

理解JavaScript的闭包是迈向高级JS程序员的必经之路,理解了其解释和运行机制才能写出更为安全和优雅的代码。

JS引用不在同一个页面中

没有import

只有document.write('<script src="B.js"><\/script>')

没有类似的方法,不过可以模拟

JavaScript code

function Import()

{

    for( var i=0; i<arguments.length; i++ )

    {

        var file = arguments;

        if ( file.match(/\\.js$/i)) 

            document.write('<script type=\\"text/javascript\\" src=\\"' + file + '\\"></sc' + 'ript>');

        else

            document.write('<style type=\\"text/css\\">@import \\"' + file + '\\" ;</style>');

    }

};

然后再a.js中 Import(b.js) 就OK

加入以下js代码

JavaScript code

function include(path){

    var a=document.createElement("script");

    a.type = "text/javascript";

    a.src=path;

    var head=document.getElementsByTagName("head")[0];

    head.appendChild(a);

    }

可以在任何地方以

include("A.js");

include("http://hcl0208.cnblogs.com/test.js");

等等来包含外部的j

js加载顺序问题

页面的加载顺序是从上至下的。

所以一般外部引入js写在最上面。 页面里面的JS写在最下面。

$(function(){...})是在页面JS加载完之后才会加载。 类似于body标签的onload事件。

运行下面的语句, 先会打印出2, 然后才是打印出1.

<html>

<script src='jquery-1.6.min.js' type="text/javascript"></script>

<script>

$(function(){alert(document.getElementById("test").value);})

</script>

<body>

<input type="text" value="1" id="test" />

</body>

<script>alert(2);</script>

posted on 2016-05-30 14:07  路过的二师兄  阅读(362)  评论(0编辑  收藏  举报