五、数组

数组是数据的有序列表,每个元素在数组中都有数字位置编号,也就是索引。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);

};

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

JS详细教程(下)的更多相关文章

  1. linux(centos 6.4)下安装php memcache服务端及其客户端(详细教程)

    前言 在搭建个人博客时,由于没有使用任何框架,纯手工code前台和后台,导致遇到许多问题,其中一个问题就是mysql连接导致的页面相应速度异常低.在查询各种途径后,只能考虑使用memcache缓存.在 ...

  2. [js高手之路]深入浅出webpack教程系列3-配置文件webpack.config.js详解(下)

    本文继续接着上文,继续写下webpack.config.js的其他配置用法. 一.把两个文件打包成一个,entry怎么配置? 在上文中的webpack.dev.config.js中,用数组配置entr ...

  3. Vue框架下的node.js安装教程

    Vue框架下的node.js安装教程 python服务器.php  ->aphche.java ->tomcat.   iis -->它是一个可以运行JAVASCRIPTR 的运行环 ...

  4. Ubuntu下安装JDK详细教程

    Ubuntu下安装JDK详细教程 作者:凯鲁嘎吉 - 博客园http://www.cnblogs.com/kailugaji/ Ubuntu版本:Ubuntu-12.04.5-desktop-i386 ...

  5. (转载)Centos下Elasticsearch安装详细教程

    原文地址:http://www.cnblogs.com/sunny1009/articles/7874251.html Centos下Elasticsearch安装详细教程 1.Elasticsear ...

  6. MAC系统下Sublime Text3 配置Python3详细教程

    MAC系统下Sublime Text3 配置Python3详细教程(亲测有效) https://blog.csdn.net/weixin_41768008/article/details/798590 ...

  7. Windows下安装MySQL详细教程

    Windows下安装MySQL详细教程 1.安装包下载  2.安装教程 (1)配置环境变量 (2)生成data文件 (3)安装MySQL (4)启动服务 (5)登录MySQL (6)查询用户密码 (7 ...

  8. 在linux下安装配置rabbitMQ详细教程

    在linux下安装配置rabbitMQ详细教程 2017年12月20日 17:34:47 阅读数:7539 安装Erlang 由于RabbitMQ依赖Erlang, 所以需要先安装Erlang. Er ...

  9. Win10 Anaconda下TensorFlow-GPU环境搭建详细教程(包含CUDA+cuDNN安装过程)(转载)

    win7(win10也适用)系统安装GPU/CPU版tensorflow Win10 Anaconda下TensorFlow-GPU环境搭建详细教程(包含CUDA+cuDNN安装过程) 目录 2.配置 ...

随机推荐

  1. 模拟Post登陆带验证码的网站

    前言: 作者在一个项目需求 模拟用户登陆,获取该用户的订单记录. 该系统需要用户名,密码,验证码 (验证码为正楷的数字4位),于是参考网络一些文章,并进行了很多测试,总结步骤如下: 步骤1 : 通过h ...

  2. Linux系统的理解及学习Linux内核的心得

    作业列表      (点击作业跳转) linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作 linux内核分析作业:操作系统是如何工作的进行:完成一个简单的时间片轮转多道程序内核 ...

  3. ABP理论学习之MVC视图

    返回总目录 本篇目录 介绍 AbpWebViewPage基类 介绍 ABP通过Abp.Web.Mvc Nuget包集成了MVC视图.因此你可以像常规那样创建MVC视图. AbpWebViewPage基 ...

  4. ABP理论学习之SignalR集成

    返回总目录 本篇目录 介绍 安装 建立连接 内置功能 你自己的SignaR代码 介绍 Abp.Web.SignalR 使得在基于ABP的应用程序中使用 SignalR相当容易.查看SignalR文档获 ...

  5. Lesson 12 Goodby and good luck

    Text Our neighbour, Captain Charles Alison, will sail from Portsmouth tomorrow. We'll meet him at th ...

  6. Hadoop学习笔记—16.Pig框架学习

    一.关于Pig:别以为猪不能干活 1.1 Pig的简介 Pig是一个基于Hadoop的大规模数据分析平台,它提供的SQL-LIKE语言叫Pig Latin,该语言的编译器会把类SQL的数据分析请求转换 ...

  7. ASP.Net MVC开发基础学习笔记:二、HtmlHelper与扩展方法

    一.一个功能强大的页面开发辅助类—HtmlHelper初步了解 1.1 有失必有得 在ASP.Net MVC中微软并没有提供类似服务器端控件那种开发方式,毕竟微软的MVC就是传统的请求处理响应的回归. ...

  8. Net作业调度(五)—quartz.net动态添加job设计

    介绍 在实际项目使用中quartz.net中,都希望有一个管理界面可以动态添加job,而避免每次都要上线发布. 也看到有园子的同学问过.这里就介绍下实现动态添加job的几种方式, 也是二次开发的核心模 ...

  9. [Unity][Heap sort]用Unity动态演示堆排序的过程(How Heap Sort Works)

    [Unity][Heap sort]用Unity动态演示堆排序的过程 How Heap Sort Works 最近做了一个用Unity3D动态演示堆排序过程的程序. I've made this ap ...

  10. JavaScript面试时候的坑洼沟洄——逗号、冒号与括号

    看完了javaScript数据类型和表达式与运算符相关知识后以为可以对JavaScript笔试题牛刀小试一把了,没想到有一次次的死在逗号,冒号和括号上,不得已再看看这几个符号吧. 逗号 逗号我们常见的 ...