zoukankan      html  css  js  c++  java
  • JS详细教程(下)

    五、数组

      数组是数据的有序列表,每个元素在数组中都有数字位置编号,也就是索引。JS中的数组是弱类型,每一项都可以保存任何类型的数据。

    创建数组

    ①使用Array构造函数

    var arr=new Array();

    var array=new Array("red","green","blue");   //传入数组应该包含的项

    var a=new Array(100);    //直接指定数组长度

    注意:数组的长度最大为4294967295


    ②使用数组字面量表示法

    var Person=['winty','lzh','LuckyWinty']   //创建一个3个字符串的数组

    var test=[1,2,]                        //不要这样,这样会创建一个包含2或3项的数组

    数组元素读写


    数组元素操作

    ①栈方法

    push()方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。而pop()方法则从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。这样结合使用可以实现类似栈的行为,请看下面的例子: 

    var colors = new Array(); 

    var count = colors.push("red", "green"); 

    alert(count); //2 

    count = colors.push("black"); 

     alert(count); //3 

    var item = colors.pop(); 

    alert(item); //"black" 

    alert(colors.length); //2 

    ②队列方法

    由于push()是向数组末端添加项的方法,因此要模拟队列只需一个从数组前端取得项的方法。实现这一操作的数组方法就是shift(),它能够移除数组中的第一个项并返回该项,同时将数组长度减1。结合使用shift()和push()方法,可以像使用队列一样使用数组。 

    var colors = new Array(); //创建一个数组 

    var count = colors.push("red", "green"); //推入两项 

    alert(count); //2 

    count = colors.push("black"); 

     alert(count); //3 

    var item = colors.shift(); //取得第一项 ú

    alert(item); //"red" 

    alert(colors.length); //2 

    ECMAScript还为数组提供了一个unshift()方法。顾名思义,unshift()与shift()的用途相反:它能在数组前端添加任意个项并返回新数组的长度。因此,同时使用 unshift()和 pop()方法,可以从相反的方向来模拟队列,即在数组的前端添加项,从数组末端移除项,如下面的例子所示。 

    var colors = new Array(); 

    var count = colors.unshift("red", "green"); 

    alert(count); //2 

    count = colors.unshift("black"); 

    alert(count); //3 

    var item = colors.pop(); 

    alert(item); //"green" 

    alert(colors.length); //2 

    重排序方法

    数组中已经存在两个可以直接用来重排序的方法:reverse()和 sort()。

    var values = [1, 2, 3, 4, 5]; 

    values.reverse(); 

    alert(values); //5,4,3,2,1

    但是sort()方法是比较字符串以确定如何排序的,即使数组中的每一项都是数值,如:

    var values = [0, 1, 5, 10, 15]; 

    values.sort(); 

    alert(values); //0,1,10,15,5 

    不用说,这种排序方式在很多情况下都不是最佳方案。因此sort()方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。 比较函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回0,如果第一个参数应该位于第二个之后则返回一个正数。以下就是一个简单的比较函数:

    function compare(value1, value2) { 

                 if (value1 < value2) { 

                       return -1;

                 } else if (value1 > value2) { 

                       return 1; 

                          } else { 

                                 return 0; 

                             } 

     } 

    这个比较函数可以适用于大多数数据类型,只要将其作为参数传递给sort()方法即可,如下面这个例子所示。 

    var values = [0, 1, 5, 10, 15]; 

    values.sort(compare); 

    alert(values); //0,1,5,10,15 

    操作方法

    合并数组:concat()

    slice()方法,有三种用法。

    删除:可以删除任意数量的项,只需指定 2个参数:要删除的第一项的位置和要删除的项数。

    插入:可以向指定位置插入任意数量的项,只需提供3个参数:起始位置、0(要删除的项数)和要插入的项。如果要插入多个项,可以再传入第四、第五,以至任意多个项。

    替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定 3个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。 

    例如:

    var colors = ["red", "green", "blue"]; 

    var removed = colors.splice(0,1); // 删除第一项 

    alert(colors); // green,blue 

    alert(removed); // red,返回的数组中只包含一项 

    removed = colors.splice(1, 0, "yellow", "orange"); // 从位置1 开始插入两项 

    alert(colors); // green,yellow,orange,blue 

    alert(removed); // 返回的是一个空数组 

    removed = colors.splice(1, 1, "red", "purple"); // 插入两项,删除一项 

    alert(colors); // green,red,purple,orange,blue 

    alert(removed); // yellow,返回的数组中只包含一项 

    join()方法将数组转为字符串:

    var arr=[1,2,3]

    arr.join("-");    //  "1_2_3"

    位置方法

    迭代方法

    every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。

     filter():对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组。 

    forEach():对数组中的每一项运行给定函数。这个方法没有返回值。 

    map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。 

    some():对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。 

    以上方法都不会修改数组中的包含的值。 

    例子:

    var arr=[1,2,3,4,5];

    arr.forEach(function(x,index,a){

                              console.log(x+"---"+index+"----"+(a===arr));

    });

    //1---0----true

    //2---1----true

    //3---2----true

    //4---3----true

    //5---4----true

    arr.map(function(x){

      return x+10;

    });    //[11,12,13,14,15]

    arr;   //[1,2,3,4,5]     原数组不会被修改

    归并方法

    reduce()和 reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。其中,reduce()方法从数组的第一项开始,逐个遍历到最后。而reduceRight()则从数组的最后一项开始,向前遍历到第一项。

    给reduce()和 reduceRight()的函数接收 4个参数:前一个值、当前值、项的索引和数组对象。

    这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。 使用reduce()方法可以执行求数组中所有值之和的操作,比如: 

    var values = [1,2,3,4,5]; 

    var sum = values.reduce(function(prev, cur, index, array){ 

                      return prev + cur;

                   }); 

    alert(sum); //15

    var sum1=arr.reduce(function(x,y){ 

                       return x+y

    },100);    

    alert(sum1)// 115,有参数传入,所以会把参数也加上。

    数组和一般对象的比较:

    相同:两者都可以继承,数组是对象,对象不一定是数组,都可以当作对象添加删除属性。

    不同:数组自动更新length,按索引访问数组常常比访问一般对象属性明显迅速。数组对象继承Array.prototype上的大量数组操作方法。

    六、函数

    函数是一块JavaScript代码,被定义一次,但可执行和调用多次。JS中的函数也是对象。所以JS函数可以像其它对象那样操作和传递。所以我们也常叫JS中的函数为函数对象。

    调用方式

    直接调用:foo();

    对象方法:o.method();

    构造器:new Foo();

    call/apply/bian:func.call(o);
    函数声明、表达式、构造器比较:

    this

    ①全局作用域上的this指向的就是this


    ②一般函数的this


    ③对象原型链上的this


    ④构造器上的this

    ⑤call/apply方法与this


    ⑥bind方法与this(IE9+才会支持该方法)


    函数属性&arguments



    函数闭包

    闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。

    闭包与变量

    作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。

    下面这个例子可以清晰地说明这个问题。 

    function createFunctions(){ 

         var result = new Array(); 

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

               result[i] = function(){ 

                          return i; 

               }; 

          } 

        return result; 

    这个函数会返回一个函数数组。表面上看,似乎每个函数都应该返自己的索引值,即位置0的函数返回0,位置1的函数返回1,以此类推。但实际上,每个函数都返回10。因为每个函数的作用域链中都保存着 createFunctions()函数的活动对象,所以它们引用的都是同一个变量 i。当createFunctions()函数返回后,变量i 的值是10,此时每个函数都引用着保存变量i的同一个变量对象,所以在每个函数内部i的值都是10。

    但是,我们可以通过创建另一个匿名函数强制让闭包的行为符合预期,如下所示。 

    function createFunctions(){ 

            var result = new Array(); 

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

                  result[i] = function(num){ 

                                  return function(){ 

                                             return num; 

                                  }; 

                  }(i);

             } 

          return result;

     } 

    在这个版本中,我们没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋给数组。这里的匿名函数有一个参数num,也就是最终的函数要返回的值。在调用每个匿名函数时,我们传入了变量i。由于函数参数是按值传递的,所以就会将变量i的当前值复制给参数num。而在这个匿名函数内部,又创建并返回了一个访问num的闭包。这样一来,result数组中的每个函数都有自己num 变量的一个副本,因此就可以返回各自不同的数值了。

    可以利用闭包的这个特性,封装函数,模仿块级作用域。

    function(){

    //这里是块级作用域

    })();

    无论在什么地方,只要临时需要一些变量,就可以使用私有作用域,例如:

    function outputNumbers(count){    

         (function () { 

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

                 alert(i);

             }

    })(); 

         alert(i);   //导致一个错误!

     }  

    在这个重写后的outputNumbers()函数中,我们在for循环外部插入了一个私有作用域。在匿名函数中定义的任何变量,都会在执行结束时被销毁。因此,变量i只能在循环中使用,使用后即被销毁。而在私有作用域中能够访问变量count,是因为这个匿名函数是一个闭包,它能够访问包含作用域中的所有变量。 这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。一般来说,我们都应该尽量少向全局作用域中添加变量和函数。在一个由很多开发人员共同参与的大型应用程序中,过多的全局变量和函数很容易导致命名冲突。而通过创建私有作用域,每个开发人员既可以使用自己的变量,又不必担心搞乱全局作用域。例如:

     (function(){

         var now = new Date();

         if (now.getMonth() == 0 && now.getDate() == 1){ 

            alert("Happy new year!");

         }

     })(); 

    把上面这段代码放在全局作用域中,可以用来确定哪一天是1月1日;如果到了这一天,就会向用户显示一条祝贺新年的消息。其中的变量now现在是匿名函数中的局部变量,而我们不必在全局作用域中创建它。 

    七、oop

    JS解释器按照如下顺序找到我们定义的函数和变量:

    1.函数参数(若未传入,初始化该参数为undefined)
    2.函数声明(若发生命名冲突,会覆盖)——函数声明提升的原因
    3.变量声明(初始化变量值undefined,若发生命名冲突,会忽略)
    在全局作用域下,函数声明和变量声明会被前置到全局执行上下文(执行环境)中。
    在浏览器环境下,当this表示全局对象时,this就指window对象
    匿名函数,加上括号就变成了函数表达式,再加个括号,就变成了立即执行函数,再在前面加个!号可以将函数声明变为函数表达式,防止函数被前置执行,留下最后那个括号...
    同一个函数,被调用多次的话,每次调用函数时都会有独立的执行上下文,每个执行上下文环境都会记录各自的变量参数等信息。

    继承

    ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的。

     实现原型链有一种基本模式,其代码大致如下。 

    function SuperType(){ 

            this.property = true;

    }

    SuperType.prototype.getSuperValue = function(){ 

              return this.property; 

    }; 

    function SubType(){

            this.subproperty = false; 

    //继承了SuperType 

    SubType.prototype = new SuperType(); 

    SubType.prototype.getSubValue = function (){ 

                   return this.subproperty;

     }; 

    var instance = new SubType(); 

    alert(instance.getSuperValue()); //true 

    借用构造函数

    在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数(constructor stealing)的技术(有时候也叫做伪造对象或经典继承)。这种技术的基本思想相当简单,即在子类型构造函数的内部调用超类型构造函数。如下所示: 

    function SuperType(){ 

                  this.colors = ["red", "blue", "green"]; 

    function SubType(){ 

     //继承了 SuperType 

     SuperType.call(this); 

    var instance1 = new SubType(); 

    instance1.colors.push("black"); 

    alert(instance1.colors); //"red,blue,green,black" 

    var instance2 = new SubType(); 

    alert(instance2.colors); //"red,blue,green" 

    组合继承

    既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。下面来看一个例子。 

    function SuperType(name){ 

                  this.name = name; 

                  this.colors = ["red", "blue", "green"]; 

    SuperType.prototype.sayName = function(){ 

            alert(this.name); 

    }; 

    function SubType(name, age){ 

     //继承属性 

     SuperType.call(this, name); 

     this.age = age; 

    }

     //继承方法 

    SubType.prototype = new SuperType(); 

    SubType.prototype.constructor = SubType; 

    SubType.prototype.sayAge = function(){ 

                   alert(this.age); 

    }; 

    var instance1 = new SubType("Nicholas", 29); 

    instance1.colors.push("black"); 

    alert(instance1.colors); //"red,blue,green,black" 

    instance1.sayName(); //"Nicholas"; 

    instance1.sayAge(); //29 

    var instance2 = new SubType("Greg", 27); 

    alert(instance2.colors); //"red,blue,green" 

    instance2.sayName(); //"Greg"; 

    instance2.sayAge(); //27 

    原型式继承

    var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; 

    var anotherPerson = object(person); 

    anotherPerson.name = "Greg"; 

    anotherPerson.friends.push("Rob"); 

    var yetAnotherPerson = object(person); 

    yetAnotherPerson.name = "Linda"; 

    yetAnotherPerson.friends.push("Barbie"); 

    alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" 

    克罗克福德主张的这种原型式继承,要求你必须有一个对象可以作为另一个对象的基础。如果有这么一个对象的话,可以把它传递给object()函数,然后再根据具体需求对得到的对象加以修改即可。在这个例子中,可以作为另一个对象基础的是person对象,于是我们把它传入到object()函数中,然后该函数就会返回一个新对象。这个新对象将person作为原型,所以它的原型中就包含一个基本类型值属性和一个引用类型值属性。这意味着person.friends不仅属于person所有,而且也会被anotherPerson以及yetAnotherPerson 共享。实际上,这就相当于又创建了person对象的两个副本。 

    ECMAScript 5 通过新增Object.create()方法规范化了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,Object.create()与object()方法的行为相同。

    var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; 

    var anotherPerson = Object.create(person); 

    anotherPerson.name = "Greg"; 

    anotherPerson.friends.push("Rob"); 

    var yetAnotherPerson = Object.create(person); 

    yetAnotherPerson.name = "Linda"; 

    yetAnotherPerson.friends.push("Barbie"); 

    alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" 

    Object.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相同:每个属性都是通过自己的描述符定义的。以这种方式指定的任何属性都会覆盖原型对象上的同名属性。

    寄生式继承

    即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。以下代码示范了寄生式继承模式。 

    function createAnother(original){ 

                  var clone = object(original); //通过调用函数创建一个新对象 

                  clone.sayHi = function(){ //以某种方式来增强这个对象 

                            alert("hi"); 

                  }; 

         return clone; 

     } / /返回这个对象 

    在这个例子中,createAnother()函数接收了一个参数,也就是将要作为新对象基础的对象。然后,把这个对象(original)传递给object()函数,将返回的结果赋值给clone。再为clone对象添加一个新方法sayHi(),最后返回clone对象。

    可以像下面这样来使用createAnother()函数: 

    var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; 

    var anotherPerson = createAnother(person); 

    anotherPerson.sayHi(); //"hi" 

    这个例子中的代码基于person返回了一个新对象——anotherPerson。新对象不仅具有person的所有属性和方法,而且还有自己的sayHi()方法。

    寄生组合式继承

    寄生组合式继承的基本模式如下所示。 

    function inheritPrototype(subType, superType){ 

                  var prototype = object(superType.prototype); //创建对象

                  prototype.constructor = subType; //增强对象

                  subType.prototype = prototype; //指定对象

     } 

    这个示例中的 inheritPrototype()函数实现了寄生组合式继承的最简单形式。这个函数接收两个参数:子类型构造函数和超类型构造函数。在函数内部,第一步是创建超类型原型的一个副本。第二步是为创建的副本添加constructor属性,从而弥补因重写原型而失去的默认的constructor属性。最后一步,将新创建的对象(即副本)赋值给子类型的原型。这样,我们就可以用调用 inherit- Prototype()函数的语句,去替换前面例子中为子类型原型赋值的语句了。

    function SuperType(name){ 

                  this.name = name;

                  this.colors = ["red", "blue", "green"]; 

    SuperType.prototype.sayName = function(){ 

                      alert(this.name); 

    }; 

    function SubType(name, age){ 

                  SuperType.call(this, name); 

                  this.age = age; 

    inheritPrototype(SubType, SuperType); 

    SubType.prototype.sayAge = function(){ 

                   alert(this.age); 

    }; 

    欢迎关注我的个人微信订阅号:前端生活
    转载请注明出处!

  • 相关阅读:
    C语言I博客作业03
    经度和纬度
    TUXEDO client端示例程序 TUXEDO自带 方便查看
    关于网络字节序和主机字节序
    TUXEDO server端示例程序 TUXEDO自带 方便查看
    TUXEDO 通讯缓冲区类型
    Android中的五大布局
    Android四大组件之Activity(活动)及其布局的创建与加载布局
    Java的内部类
    Android单元测试
  • 原文地址:https://www.cnblogs.com/LuckyWinty/p/5267635.html
Copyright © 2011-2022 走看看