zoukankan      html  css  js  c++  java
  • 【JavaScript】JS总结 – 乱

    一、 重要:js中的function函数声明、函数表达式

    // 函数声明    
    // Ex: 会在代码执行之前提前加载到作用域中,即js解析器会优先读取,确保在所有代码执行之前声明已经被解析;所以可以在定义之前调用。
    function test(){
        document.write("test() invoke!" + "<br>");
    }
    // 函数表达式    
    // Ex: 在代码执行到那一行的时候才会有定义;定义之后才能调用。
    var t2 = function(){
        document.write("t2() invoke!" + "<br>");
    }
    //函数声明:会在代码执行之前提前加载到作用域中
    test("1"+name);    // test() invoke!14
    var name =1;
    function test(name){
        document.write("test() invoke!" + name + "<br>");
    }
    name=2;
    test("2"+name); //test() invoke!22
    
    //函数表达式:在代码执行到那一行的时候才会有定义
    var t2;
    //t2();    // t2 is not a function
    test("21"+name);    //test() invoke!212
    name =3;
    test("22"+name);    //test() invoke!223
    t2 = function(){
        document.write("t2() invoke!" + "<br>");
    }
    
    t2();    //正确 t2() invoke!
    
    name=4;
    test("3"+name);    //test() invoke!34

    分析

    1. 为什么是 test() invoke!14?

        Js顺序逐行解析完后,name=4, 所以是 test() invoke!14

    2. 执行test("2"+name);前 name =2;

    // 函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用 
     
    fnName();
    function fnName(){
        ...
    }//正常,因为‘提升’了函数声明,函数调用可在函数声明之前
     
    fnName();
    var fnName=function(){
     ...
    }//报错,变量fnName还未保存对函数的引用,函数调用必须在函数表达式之后
    
    var fnName;
    fnName();
    fnName =function(){
     ...
    }//报错,变量fnName还未保存对函数的引用,函数调用必须在函数表达式之后
     
    var fnName=function(){
        alert('Hello World');
    }();//函数表达式后面加括号,当javascript引擎解析到此处时能立即调用函数
     
    function fnName(){
        alert('Hello World');
    }(1);//不会报错,但是javascript引擎只解析函数声明,忽略后面的括号,函数声明不会被调用
     
    function(){
        console.log('Hello World');   
    }();//语法错误,虽然匿名函数属于函数表达式,但是未进行赋值操作,
    //所以javascript引擎将开头的function关键字当做函数声明,报错:要求需要一个函数名
    // 问题:   它们之间的区别是什么?都会解析到就立即调用,是只参数位置不同。
    
    (function test(arg){
        document.write(arg + "<br>");
    }("111"));    //传说中推荐写法
    
    (function test(arg){
        document.write(arg + "<br>");
    })("222");    //Jquery写法

    二、 Js中的私有作用域 和闭包

    资料:http://www.jb51.net/article/24101.htm

    闭包:在内部定义的函数和变量只能在此范围内有效。形成是否是私有变量的概念。

    var i=3;
    function init(pa){
        alert("外层init:"+i+","+pa);
    }
    
    (function(pa) {
    //私有作用域
    var i=2; 
    //i=2;//这不是私有变量,会修改外层的i
        function init(){
            alert("内层init:"+i+","+pa);
        }
        init(); //内层init:2,lyn
    })('lyn');
    
    init("vergil");//外层init:3,vergil

    闭包含义:

    闭包就是能够读取其他函数内部变量的函数。

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

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

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

    什么是闭包:

    function f1(){
        var n=999;
        add=function(){//没用var声明,表示是全局变量。 匿名表达式
            n+=1;
    }
        function f2(){
            document.write(n+"<br>");
        }
        return f2;
    }
    var result=f1();//result指向的是f1()的返回值,不是f1,二是f2的匿名
    result();// 999    
    add();
    result();// 1000

    f2函数就是闭包,f2可以访问f1的内部变量n。

    函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

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

    当内部函数在定义它的作用域的外部被引用时,就创建了该内部函数的闭包;

        如ex,f2定义在f1内部,但在f1的外部被引用了,既result();

    如果内部函数引用了外部函数的变量,当外部函数调用完毕后,这些变量在内存不会被释放,因为闭包需要它们. 

        如ex,f2函数内部使用了外部的n;在var result=f1();调用完后。result指向f2,f1()结束调用。但此时不会释放局部变量n;因为n还被f2所用。

    闭包注意事项

    1) 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。

        解决方法: 在退出函数之前,将不使用的局部变量全部删除。

    2) 闭包会在父函数外部,改变父函数内部变量的值。

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

    通过Jquery源码理解闭包/私有变量:

    // Jquery的定义可以理解为:
    (function(){
        var a=...
    })(jQuery);
    // 此时,保护了内部的a不会被外部/全局变量修改。

    几个理解js闭包的示例:

    1、

    var name = "The Window";   
    var object = {  
      name : "My Object",  
      getNameFunc : function(){  
        return function(){  
          return this.name;  
       };   
      }   
    };  
    alert(object.getNameFunc()());  //The Window

    解析: object.getNameFunc()()实质, var temp = object.getNameFunc();

        temp() 等价于 window.temp(); // 此时调用的this是window,即全局变量

    2、

    function outerFun()
    {
     var a=0;
     function innerFun()
     {
      a++;
      alert(a);
     }    
    }
    innerFun();  //error

    解析:outerFun外部无法调用内部方法。

    3、

    function outerFun()
    {
     var a=0;
     function innerFun()
     {
      a++;
      alert(a);
     }
     return innerFun;  //注意这里
    }
    var obj1=outerFun();
    obj1();  //1
    obj1();  //2
    var obj2=outerFun();
    obj2();  //1
    obj2();  //2

    解析: JS对象之间是独立的

    三、 Js中的私有变量 (结合闭包理解)

    资料:http://www.cnblogs.com/zhaobing/archive/2014/07/10/3836698.html

    1. 特权函数构造器

        name属性在每个对象中都有一份,不会引起共享错误,getName和setName方法是用闭包形式访问name变量,这样避免了外部环境对Person的name变量的访问,只能通过函数对Person的name变量访问;

    function Person(name){
        this.getName = function(){
            return name;
        };
        this.setName = function(value){
            name = value;
        };
    }
    var person = new Person('Lyn');
    document.write(person.getName()+"<br>");    //Lyn
    person.setName('Vergil');
    document.write(person.getName()+"<br>");    //Vergil
    //构造函数模式实现私有变量(通过闭包实现)
    function Person(nameStr){
        var name = nameStr;//私有变量
        this.age = 15;//公有变量
        this.getName = function(){
            return name;
        };
        this.setName = function(value){
            name = value;
        };
        this.getInfo = function(){
            document.write(name + "  " +this.age + "<br        //Lyn 15>");
        };
    }
    var p1 = new Person('Lyn');
    var p2 = new Person('Vergil');
    p1.getInfo();        //Lyn 15
    p2.getInfo();        //Vergil 15
    p1.setName('VergiLyn');    
    p1.getInfo();        //VergiLyn 15
    p2.getInfo();        //Vergil 15

    问题: 每次调用时都会创建函数对象,耗费资源,函数不可以共享。

    2. 静态变量 (其实就是单例)

    //静态私有变量
    (function() {
        var name = "NULL";//静态私有,被所有实例所共享
        //js中不加var其实就是定义全局变量
        Person = function(value) { //默认为全局变量了 ,可以被外部访问       
            name = value;
        };
        Person.prototype.getName = function() {
            return name;
        };
        Person.prototype.setName = function(value) {
            name = value;
        };
    })();
    
    var person1 = new Person("Lyn");
    document.write(person1.getName()+ "," + name+"<br>"); //"Lyn",""
    person1.setName("Dante");
    document.write(person1.getName()+ "," + name +"<br>"); //"Dante",""
    
    var person2 = new Person("Vergil");
    document.write(person1.getName()+","+name+"<br>"); //"Vergil",""
    document.write(person2.getName()+","+name+"<br>"); //"Vergil",""
    document.write(sss); //error:sss undefined

    注意:可以访问到name,但值并不是想象中的Lyn、Dante,也不是初始值"NULL"或者undefined。

        忘记当初这js在哪测试的了,但我在写的时候去w3s和runoob测试了下,貌似结果不一样。

    四、 Js中的true/false

    false:undefined、null、false、""、0

    typeof undefined // undefined

    typeof null // object

    null === undefined // false

    null == undefined // true

    五、 Js中的 typeof

    typeof 一般只能返回如下几个结果:

         number,boolean,string,function,object,undefined

    ex:

        typeof "John" // 返回 string

        typeof 3.14 // 返回 number

        typeof false // 返回 boolean

        typeof [1,2,3,4] // 返回 object

        typeof {name:'John', age:34} // 返回 object

    六、 Js中的 === 、==

    // ===:类型 和 值 全相等;
    
    // ==:只比较值相等;
    
    null === undefined // false
    
    null == undefined // true
    
    var o = new Object("s"); //object
    
    var s = "s"; //string
    
    /** 注意Jquery提供的方法的趋避 */
    
    document.write($.type(o) + "<br>");// string
    
    document.write((typeof o ) + "<br>");// object
    
    document.write($.type(s) + "<br>");// string
    
    document.write((typeof s ) + "<br>");// string
    
    document.write((o == s) + "<br>"); // true
    
    document.write((o === s) + "<br>"); // false

    七、 Js中的call / apply

    call / apply的作用是一样的,都是js内置。

    区别: 参数形式不一样;

        *.call(obj,arg1...)

        *.apply(obj,argArray)

    作用:

        Ex:

            var temp = new TempObj();

            var lyn = new LynObj(1,"vergilyn");

          temp.tempMethod(lyn,arg1,arg2)

        TempObj()中存在方法tempMethod(arg1,arg2)

        LynObj()中不存在,现在用LynObj对象的this去调用TempObj的tempMethod()方法。

        即,temp内部的this指针不指向temp而是lyn。

    八、 重要:js中的prototype

        函数运行时会先去本体的函数中去找,如果找到则运行,找不到则去prototype中寻找函数。(或者可以理解为prototype不会克隆同名函数)

        访问顺序,先 本体 -> 原型链prototype ;当在本体找到同名时,就调用了,不会再去寻找prototype的了。

    var LynObj = function(id,name){
            this.name = name;
            this.id = id;
            this.show=function(){
                alert("本体中同名的show():"+this.name);
            }
        }
        //LynObj的原型 要写在 new LynObj()之前
        LynObj.prototype={
            age:24,
            id:-1,
            show:function(){}, //本体中有,不会调用此方法
            add:function(a1,a2){
                alert("add: " + a1 + " + " + a2 +" = " + (a1+a2));
            }
        };
        var lyn = new LynObj(1,"vergilyn");
        lyn.show();        //本体中的同名show():vergilyn
        alert(lyn.age);    //24
        alert(lyn.id);    //1,对象本身的,不是prototype中的
        lyn.add(2,4);    //add: 2 + 4 = 6

    问题:  1、 如果想调用prototype中的同名方法, 以上写法是不可以的。

    解决:

    var TempObj = function(){  //为了能调用prototype中的全部属性/方法
        this.age = 24;
        this.name = "dante";
        this.show = function(){
            alert("prototype中同名的show():"+this.name);
        },
        this.add = function(a1,a2){
            alert("add: " + a1 + " + " + a2 +" = " + (a1+a2));
        }
    };
    LynObj.prototype= new TempObj();
    
    var temp = new TempObj()
    var lyn = new LynObj(1,"vergilyn");
    lyn.show();        //本体中的同名show():vergilyn
    alert(lyn.age);    //24
    alert(lyn.name);//vergilyn
    lyn.add(2,4);    //add: 2 + 4 = 6
    temp.show.call(lyn);//prototype中同名的show():vergilyn
    
    /* call 或者 apply 是js内置; 把temp中的this改为lyn对象,调用show()
    *  区别: call(obj,arg1...), apply(obj,argArrays)
    */

    九、 JS中的for-in / in, 形式 for(name in obj) / name in obj

    for-in循环遍历对象/数组,name表示对象的属性/数组的下标。

    in判断name是否是obj的直接属性。返回true/false

       注:in判断的是直接属性,见ex

    var obj = {"a1":{"b1":"b1","b2":"b2"},"a2":"a2","a3":"a3"};
    for(name in obj){
        document.write(name + ":" + obj[name] + "<br>");
        // a1:[Object] a2:a2 a3:a3
    }
    
    var arr = [1,3,5];
    for(index in arr){
        document.write(index + ":" + arr[index] + "<br>");
        //0:1   1:3   2:5 
    }
    document.write(("b1" in obj) + "<br>");         // false
    document.write(("b1" in obj.a1) + "<br>");     // true
    
    document.write(("a3" in obj) + "<br>");        // true
    document.write(("a4" in obj) + "<br>");        // false
    
    // "1" / 1 都表示下标  js的2中调用 obj.name 等价于 obj[name]
    document.write(("1" in arr) + "<br>");        // true  
    document.write((1 in arr) + "<br>");         // true

    十、 Jquery的扩展extend

    1、 jQuery.extend: jQuery类的 类方法;对jQuery类扩展, 只需 jQuery.extend(...);

    /** 为jQuery类扩展的一些类方法。Jquery部分源码 */
    jQuery.extend({
        expando:"jQuery"+(version+Math.random()).replace( /D/g, "" ),
    isFunction: function( obj ) {
            return jQuery.type( obj ) === "function";
        },
    isEmptyObject: function( obj ) { //判断是不是empty对象, 
            var name;  // name = undefined
            for ( name in obj ) {return false;}
            return true;
        }
    )}

    2、 jQuery.fn.extend: 因为 jQuery.fn = jQuery.prototype; 所以定义了jQuery的对象方法; 对jQuery对象扩展,只需 jQuery.fn.extend(...);

    jQuery.fn.extend( {
        filter: function( selector ) {
            return this.pushStack( winnow( this, selector || [], false ) );
        },
        not: function( selector ) {
            return this.pushStack( winnow( this, selector || [], true ) );
        }
    )}

    特别

        1. 根据实际的参数,判断是对jQuery的扩展(不管是类/对象),还是这是个工具类。

        2. 区分深复制/浅复制的区别。

            深复制:会递归处理对象中的对象。

            浅复制:(可理解为)把对象中的对象当作是属性。

    // jQuery-1.12.3 sourcecode:
    jQuery.extend = jQuery.fn.extend = function() {
        var src, copyIsArray, copy, name, options, clone,
            target = arguments[ 0 ] || {},    //arguments js函数内置属性, 表示函数实际接收到的参数。类似数组,实质不是数组
            i = 1,
            length = arguments.length,
            deep = false;    //是否深复制
            
        // Handle a deep copy situation
        // Lyn:判断深复制情况   ; arguments[0] true深复制  false浅复制
        /** 深复制 浅复制 区别: 深复制会递归处理复杂对象  
         *     ex:
         *         var dest={};
         *         var result=$.extend( true/false,  dest,  
                        { name: "John", location: {city: "Boston",county:"USA"} },  
                        { last: "Resig", location: {state: "MA",county:"China"} } ); 
                        
         *      深复制: dest = {name:"John",last:"Resig",
         *                        location:{city:"Boston",state:"MA",county:"China"}}
         *
         *      浅复制: dest = {name:"John",last:"Resig",
         *                        location:{state:"MA",county:"China"}}
         * 
         */
        if ( typeof target === "boolean" ) {
            deep = target;
            target = arguments[ i ] || {};//i默认1
            i++;
        }
    
        // Handle case when target is a string or something (possible in deep copy)
        // 当target不是object 或者 function 时,设置成{} 空内容的对象
        if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
            target = {};
        }
    
        // 判断是不是对jQuery自身进行扩展。 
        // jQuery.extend()   : 扩展jQuery类
        // jQuery.fn.extend(): 扩展jQuery对象
        if ( i === length ) {
            target = this; //this指 jQuery(类) 或者 jQuery.fn(jQuery实际的对象);所以,此if表示判断对jQuery自身的扩展(类/对象)
            i--;
        }
    
        for( ; i < length; i++ ) {//因为数组循环,所以arguments后面的值覆盖之前的
    
            // Only deal with non-null/undefined values
            if ( ( options = arguments[ i ] ) != null ) { 
    //同时赋值临时变量options    
                // Extend the base object ****核心的继承逻辑 for循环****
                for ( name in options ) {    
                    src = target[ name ];     //可以理解为 json的 target.name
                    copy = options[ name ];
                    // Prevent never-ending loop 防止无限循环
                    if ( target === copy ) {
    //目标对象和要拷贝的对象  恒相等,就(不复制)执行下一个循环 
                        continue;
                    }
    //Recurse if we're merging plain objects or arrays递归如果我们合并对象/数组
                    if ( deep && copy  
                          && ( jQuery.isPlainObject(copy) 
                                || (copyIsArray = jQuery.isArray(copy)) 
    //同时赋值临时变量copyIsArray
                             ) 
                        ) {    //深复制  用递归处理合并的如果是  对象 或 数组
    
                        if ( copyIsArray ) {
                            copyIsArray = false;
                            clone = src && jQuery.isArray( src ) ? src : [];
    
                        } else {
                            clone = src && jQuery.isPlainObject( src ) ? src : {};
                        }
    
    // Never move original objects, clone them 从不移动原始对象, 把新的克隆进去
                        // Lyn: 递归处理  对象 或 数组
                        target[ name ] = jQuery.extend( deep, clone, copy );
    
                    // Don't bring in undefined values 不增加一个undefined的值
                    } else if ( copy !== undefined ) { //浅复制  
                        target[ name ] = copy;
                    }
                }
            }
        }
        return target;//返回自定义的目标对象,  
    // 注:因为target = arguments[0/1]所以参数0/1如果是对象,其对象结构会被改变
    // 或者 为了扩展jQuery,Jquery对象被改变
    };
  • 相关阅读:
    [Lydsy1706月赛]大根堆
    某考试 T1 Function
    [SHOI2016] 随机序列
    某考试 T1 至危警告
    某考试 T2 yja
    bzoj1880 [Sdoi2009]Elaxia的路线
    bzoj1804 [Ioi2007]Flood 洪水
    bzoj4546 codechef XRQRS
    bzoj4547 小奇的集合
    bzoj1443 [JSOI2009]游戏Game
  • 原文地址:https://www.cnblogs.com/VergiLyn/p/5994773.html
Copyright © 2011-2022 走看看