网站地图    收藏   

主页 > 前端 > javascript >

javascript高级程序设计ES5快速查找版

来源:未知    时间:2022-09-20 18:44 作者:小飞侠 阅读:

[导读] JavaScript高级程序设计(ES5) JavaScript简介 JavaScript简史 诞生于1995年,是一种客户端语言 JavaScript组成部分 核心(ECMAScript) ,它定义了以下规范 语法 类型 语句 关键字 保留字 操作符 对象 文档...

JavaScript高级程序设计(ES5)

JavaScript简介

  

JavaScript简史

    

诞生于1995年,是一种客户端语言

  

JavaScript组成部分

    

核心(ECMAScript) ,它定义了以下规范

      

语法

      

类型

      

语句

      

关键字

      

保留字

      

操作符

      

对象

    

文档对象模型(DOM),它经历了以下变化

      

DOM1

      

DOM2

      

DOM3

    

浏览器对象模型(BOM)

  

小结

    

ECMAScript,由ECMA-262定义,提供核心语言功能

    

DOM文档对象模型,提供访问和操作网页内容的方法和接口

    

BOM浏览器对象模型,提供与浏览器交互的方法和接口

在HTML中使用JavaScript

  

script标签中间来写js代码

  

script标签的src属性用来引用外部js文件

  

script标签的属性

    

async,只对外部文件有效,表示异步执行

    

defer,只对外部文件有效,表示延迟执行

    

src,引用外部js文件

    

type,可不写,默认值是 text/javascript

  

小结

基本概念

  

语法

    

区分大小写——变量、函数和操作符都区分大小写

    

标识符

      

定义

        

标识符就是:变量、函数、属性的名字或者函数的参数

      

命名规则

        

首字母必须是字母、下划线或$符号

        

其他字符可以是字母、下划线、$符号或数字

        

识符中的字母也可以包含扩展的ASCII或 Unicode字母字符,但是不推荐这么做

      

格式

        

驼峰大小写格式,例如:sayHelloMessage

    

注释

      

单行注释 //

      

多行注释
/*

 */

    

严格模式

      

使用 use strict

      

在整个脚本中使用,则在文件第一行 使用

      

在方法上使用,则在方法体的第一样使用

    

语句

      

使用;代表一个语句结束,如果没有分号,则由解析器自动判断

      

一个语句往往使用 { }包裹起来,这也是推荐使用的方式,以便代码解构清晰

  

关键字和保留字

    

关键字

      

关键字不能用于标识符

      

关键字有哪些

          

保留字

      

保留字不能用作标识符

      

保留字有哪些

        

变量

    

变量定义

      

变量是松散类型的或者说是弱类型的,它可以保留任何类型的值

    

变量赋值

      

定义的时候直接赋值 —— var a = 1;

      

先定义,后赋值 —— var a; a = 1;

      

修改变量值的同时,修改变量的类型 —— var a = 1; a = 'hello';  正确,但不推荐

      

同时给多个变量赋值(初始化不初始化均可),中间用逗号隔开 —— var a = 1,b=2,c=3;

  

数据类型

    

基本数据类型

      

Null

        

表示没有对象引用

      

Undefined

        

表示该变量未声明,或者声明了,但没有赋值

        

如果变量没有赋值,直接输出结果,则会报错。比如 alert( a );//直接报错

        

如果变量没有声明或者声明了但没有赋值,则使用 typeof 来判断,直接返回undefined

      

Number

        

在进行计算的时候,所有八进制和十六进制的数值最终都会被转换成十进制的数值。

        

浮点数值,会有精度问题,是因为IEEE754浮点计算的通病,比如:0.1+0.2 = 0.3000000000000004

                

数值范围

          

最大值:Number.MAX_VALUE
最小值:Number.MIN_VALUE
如果某次计算的值超过了JavaScript的数值范围,则该值自动转换为特殊的Infinity值,如果这个值是负值,则是 -Infinity(负无穷),如果这个值是正值,则是 Infinity(正无穷)
isFinite()函数用来检测数值是否位于最大值和最小值之间,例如 isFinite(100) //true

        

NaN

          

定义:NaN表示非数值,是一个特殊的值,NaN与任何值都不相等,与它自身也不相等,NaN==NaN //false

          

isNaN()函数用来判断不是数值,该函数接收到一个值时,会尝试将这个值转为数值,任何不能被转为数值的值都会返回true

            

isNaN(NaN);//true
isNaN(10); //false
isNaN(“10”);//false (可以被转换成数值10)
isNaN(“blue”);//true (不能转换成数值)
isNaN(true);   //false  (可以被转换成数值1)

            

尽管有点不可思议,但isNaN()确实也适用于对象。在给予对象调用isNaN()函数时,
会首先调用对象的valueOf()方法,然后确定该方法返回的值是否可以转换为数值。
如果不能,给予这个返回值在调用toString()方法,在测试返回值。而这个过程也是
ECMAScript中内置函数和操作符的一般执行流程。

        

数值转换,Number(),parseInt(),parseFloat()

          

Number()

            

(1)Number()函数的转换规则如下:
如果是Boolean值,true和false分别转为 1和0
如果是数字值,直接返回
如果是null值,则返回0
如果是undefined,则返回 NaN
如果是字符串,则遵循如下规则
  如果字符串只包含数字(包括前面带正号和负号的情况),则将其转换为十进制。比如 “1”会变成1,“011”变成11, “+123”会变成 123
  如果字符串中包含有效的浮点格式,则返回 浮点数值,比如 “0.12”则返回 0.12 “012.3” 变成 12.3
  如果字符串包含有效的十六进制,则将其转换为相同大小的10进制返回。
  如果字符串是空的,也即 “”,则返回 0
  如果字符串中包含除了上述格式外的字符,则将其转换为NaN。比如 Number(“123a”)//结果NaN
如果是对象,则调用对象的valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再次依照前面的规则转换返回的字符串值。

          

parseInt()

            

(2)parseInt()函数更多的是看是否符合数值模式,它的转换规则如下:
如果是第一个字符是数字,则会继续解析第二个字符,直到解析完后面的字符,或者遇到了一个非数字字符。例如:parseInt(“123a”)  //123
如果字符串前面有空格,则会忽略空格。比如 parseInt(“  123”);//123,这个和Number()一致
浮点类型的字符串,会被转为 整数,即去掉小数点以及小数点后面的字符,这一点和Number()不同。
字符串是空的,则会返回NaN,这点和 Number不同
null也会转为NaN,这点和Number不同
如果第一个字符不是数字字符或正号或负号,则返回NaN,这与Number()表现一致

          

parseFloat

            

(3)parseFloat()函数
与parseInt很类似
第一个区别:对于第一个小数点有效,后面的小数点就无效了,比如 parseFloat(“3.12.2”);结果:3.12
第二个区别:parseFloat始终忽略前导的零。十六进制的字符串会始终为0  比如: parseFloat(“0xA”);//结果:0 parseFloat(“0908.5”);//结果908.5
第三点要注意:如果字符串包含一个可解析为整数的数,parseFloat会返回整数。

      

Boolean

        

Boolean类型的字面值,只有两个 true和false,区分大小写

        

可以通过Boolean()将其它类型的值转为 true或false,例如  Boolean("hello world");//true

                

在if条件语句中,会自动执行Boolean的转换,这个非常重要

          

                

String

        

定义:字符串可以用双引号或单引号表示,但是必须成对出现,比如var a='hello';或者 var a = "hello";

        

字符字面量

          

可以出现在字符串的任意位置,而且也将被作为一个字符来解析,例如:
var text = “This is the letter sigma:\\u03a3.”  其中的 \\u03a3只表示一个字符,它的length = 1,所以 text.length = 28

          

字符字面量
\\n   换行
\\t   制表
\\b   退格
\\r   回车
\\f   进纸
\\\\   斜杠,其中前面的\\是转义符
\\’   单引号
\\“   双引号

        

字符串特点——不可变

          

ECMAScript中的字符串是不可变的,也就是说,字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量,例如:
var lang = “Java”
lang = lang + “Script”
以上示例中的变量lang开始时包含字符串“Java”.而第二行代码把lang的值重新定义为 “Java” 与 “Script”的组合,即“JavaScript”。实现这个操作的过程如下:首先创建一个能容纳10个字符的新字符串,然后把这个字符串中填充 “Java”和“Script”,最后一步是销毁原来的字符串“Java” 和 字符串 “Script”。

        

解决字符串拼接的性能问题

          

如何解决字符串拼接的性能问题:
方法是:使用数组保存,然后使用 join方法拼接即可。
如下:
var arr = [];
var oldStr1 = “Java”;
var oldStr2 = “Script”;
arr.push(oldStr1);
arr.push(oldStr2);
var finalStr = arr.join(“”);

        

转换为字符串

          

toString方法

            

var age = 11;
var ageString = age.toString(); //字符串 “11”
toString方法,还可以传入一个参数,用来指定要转换的进制数
例如 var num = 10;  num.toString(); //无参数,与参数10同价,都是将数字转为10进制的字符串
     num.toString(2);  //“1010”,把10转为了2进制的字符串。

          

String()函数

            

String()函数,使用情况是,不知道要转换的是不是null或undefined.
String()能将任何类型的值转换为字符串
如果值是toString()方法,则调用该方法(没有参数)并返回相应的结果;
如果值是null,则返回 “null”
如果值是undefined,则返回 “undefined”

              

如下:
var value1 = 10;
var value2 = true;
var value3 = null;
var value4;
console.log(String(value1));  //“10”  等价于 value1+ “”;
console.log(String(value2));  // “true”  等价于 value2+ “”;
console.log(String(value3));  // “null”  等价于 value3+ “”;
console.log(String(value4));  // “undefined” 等价于 value4+ “”;

          

+""  与String()函数功能相同

      

Symbol

        

es6新增的

    

引用数据类型

      

Object

        

定义

          

ECMAScript中的对象其实就是一组数据和功能的集合。对象可以通过执行new操作符后跟要创建的对象类型名称来创建。而Object类型的创建方式如下:
var o = new Object();
var o= new Object;虽然没问题,但是推荐

        

Object的每个实例都具有下列属性和方法

          

(由于在ECMAScript中Object是所有对象的基础,因此所有对象也具有这些基本的属性和方法):
constructor:保存着用于创建当前对象的函数。对于前面的例子,构造函数(constructor)就是Object();
hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例如: o.hasOwnProperty(“name”))
isPrototypeOf(object):用于检查传入的对象是否是当前对象的原型。
propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用for-in语句来枚举。
toLocalestring():返回对象的字符串表示,该字符串与执行环境的地区对应。
toString():返回对象的字符串表示
valueOf():返回对象的字符串、数值或布尔值表示。通常与toString()方法的返回值相同。

      

Array

      

Function

  

操作符

    

一元操作符

      

前置++和前置--   变量的值在求值以前改变

        

var age = 10
++age;    
console.log(age); //11
var age = 10;
var age1 = ++age +10;
console.log(age1); //21

      

后置++和后置--  变量的值在求值以后改变

        

var age = 10
age++;     相当于 age = age+1;
console.log(age); //11
var age = 10;
var age1 = age++ +10;  //注意,这里+10操作,因为age是求值之后改变所以 10+10 = 20;
console.log(age1); //20
注意理解:
var age = 10; age++;  var age1 = ++age + 10;   //age1 = 22;
var age = 10; age++;  var age1 = age++ + 10;   //age1 = 21;

          

注意理解:\nvar age = 10; age++;  var age1 = ++age + 10;   //age1 = 22;\nvar age = 10; age++;  var age1 = age++ + 10;   //age1 = 21;

        

所有这四个操作(前置++,前置--,后置++,后置--)对任何值都适用,也就是它们不仅适用于整数,还可以用字符串、布尔值、浮点数值和对象。在应用于不同的值时,++ --操作符遵循下列规则
在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减操作。
在应用于一个不包含有效数字字符的字符串时,将变量的值设置为NaN。
在应用于布尔值时候,false转为0,true转为1,再进行运算。
在应用于对象时候,先调用对象的valueOf()方法,然后对该值应用前面的规则,如果结果为NaN,则再调用toString()方法

var s1 = “2”;
var s2 = “z”;
var b = false;
var f = 1.1;
var o = {
valueOf:function(){
return -1;
}
}
s1++;  //3
s2++;  //NaN
b++;   //1
f--;   //0.10000000009(由于浮点舍入错误导致)
o--;   //-2

      

加减操作符

        

对于数值,前面添加+号不会对结果产生任何影响。前面添加-号,则正数变负数,负数变正数
对于非数值应用时,false转0,true转1,对象会先调用valueOf和(或)toString方法

          

var s1 = “01”;
var s2 = “1.1”;
var s3 = “z”;
var b = false;
var f = 1.1;
var o = {
valueOf:function(){
return -1;
}
}

s1 = +s1;   //1     如果使用 减号   s1 = -s1;   //-1  
s2 = +s2;   //1.1  如果使用 减号  s2 = -s2;   //-1.1
s3 = +s3;   //NaN       如果使用 减号  s3 = -s3;  //NaN
b = +b;   //0          如果使用 减号  b = -b;    //0
f = +f;   //1.1        如果使用 减号  f = -f;    //-1.1
o = +o;   //-1         如果使用 减号  o = -0;    //值变成了数值1

    

位操作符

    

布尔操作符

      

逻辑非 !   它实际上是Boolean()的取反,双重逻辑非 !! 则效果等同于 Boolean()

        

alert(!false);   //true
alert(!“blue”);           //false
alert(!0);    //true
alert(!NaN);    //true
alert(!“”);    //true
alert(!123456);   //false

      

逻辑与 &&

        

true&&true   => true
true&&false  => false
false&&true  => false
false&&false => false

        

有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值;此时,它遵循下列操作:

如果第一个操作数是对象,则返回第二个操作数
如果第二个操作数是对象,则只有在第一个操作数的求值结果为true的情况下才会返回该对象。
如果两个操作数都是对象,则返回第二个操作数
如果第一个操作数是null,则返回null
如果第一个操作数是undefined,则返回undefined

        

逻辑与属于短路操作
即如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。对于逻辑与操作而言,如果第一个操作数是false,则无论第二个操作数是什么值,结果都不再可能是true了。如果第一个为true,则会继续对第二个求值。

      

逻辑或 ||

        

true||true   => true
true||false  => true
false||true  => true
false||false => false

        

有一个操作数不是布尔值的情况下,逻辑或操作就不一定返回布尔值;此时,它遵循下列操作:

如果第一个操作数是对象,则返回第一个操作数
如果有一个操作数的求值结果为false,则返回第二个操作符
如果两个操作数都是对象,则返回第二个操作数
如果两个操作数都是null,则返回null
如果两个操作数都是NaN,则返回NaN
如果两个操作数都是undefined,则返回undefined、

        

逻辑或属于短路操作,即如果第一个操作数结果为true,就不会进行第二个操作数的求值了

我们可以利用逻辑或的这个行为,来为变量赋null或者undefined值。
alert(!123456); 
var myOjbect = preferredObject || backupObject
这个例子中,变量myObject将被赋予等号后面两个值中的一个。变量preferredObject中包含优先赋给变量myObject的值,变量backupObject 负责在preferredObject中不包含有效值的情况下提供后备值。如果preferredObject的值不是null,那么它的值将被赋给myObject;如果是null,则将backupObject的值赋给myObject.
ECMAScript程序的赋值语句经常会使用这种模式。

    

乘性操作符 (乘法、除法、取余)

      

乘法

        

乘法操作符由一个星号(*)表示,用于计算两个数的乘积。例如 var result = 34*56;

在处理特殊的情况下,乘法操作符遵循下列特殊的规则:

如果操作数都是数值,执行常规的乘法计算,即两个正数或两个负数相乘的结果还是正数,而如果只有一个操作数有符号,那么结果就是负数。如果乘积超过了ECMAScript数值的表示范围,则返回Infinity或-Infinity;
如果有一个操作数是NaN,则结果是NaN
如果是Infinity与0相乘,则结果是NaN
如果是Infinity与非0数值相乘,则结果是Infinity或-Infinity,取决于符号操作数的符号
如果是Infinity与Infinity相乘,则结果是Infinity。
如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则。

      

除法

        

除法操作符由一个斜线符号( / )表示,执行第二个操作数除第一个操作数的计算。例如:var result = 66/11;

在处理特殊的情况下,除法操作符遵循下列特殊的规则:
如果操作数都是数值,执行常规的除法计算,即两个正数或两个负数相除的结果还是正数,而如果只有一个操作符由符号,那么结果就是负数。如果商超过了ECMAScript数值的表示范围,则返回Infinity或-Infinity;
如果有一个操作数是NaN,则结果是NaN;
如果是Infinity被Infinity除,则结果就是NaN
如果是零被零除,则结果是NaN
如果是非零的有限数被零除,则结果是Infinity或-Infinity,却绝育有符号操作数的符号;
如果是Infinity被任何非零数值除,则结果是Infinity或-Infinity,取决于有符号操作数的符号。
如果有一个操作数不是数值,则在后台调用Number() 将其转换为数值,然后再应用上面的规则。

      

取余

        

求模(余数)操作符由一个百分号(%)表示,例如: var result = 26%5;//等于1

求模操作符遵循如下特殊规则
如果操作数都是数值,则执行常规的除法计算,返回除得的余数
如果被除数是无穷大值而除数是有限大的数值,则结果是NaN
如果被除数是有限大的数值而除数是零,则结果是NaN
如果是Infinity被Infinity除,则结果是NaN
如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数
如果被除数是0,则结果是0
如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则。

    

加性操作符(加法、减法)

      

加法

        

加法操作符( + )的用法  例如: var result = 1 + 2;

如果两个操作符都是数值,执行常规的加法计算,然后根据下列规则返回结果:
如果有一个操作数是NaN,则结果是NaN;
如果是Infinity加Infinity,则结果是Infinity;
如果是-Infinity加-Infinity,则结果是-Infinity;
如果是Infinity加-Infinity,则结果是NaN;
如果是+0加+0,则结果是 +0
如果是-0加-0,则结果是 -0
如果是+0加-0,则解雇是 +0
如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来
如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后将这两个字符串拼接
如果有一个操作数是对象、数值或布尔值,则调用它们的toString()方法取得相应的字符串值,然后再应用前面关于两条关于字符串的规则,对于undefined和null,则分别调用String()函数并取得字符串 “undefined”和
“null”
   
var result1 = 5 + 5;//10
var result2 = 5 + “5”;//55
var num1 = 5;
var num2 = 10;
var message = “The sum of 5 and 10 is”+ num1+num2;//The sum of 5 and 10 is 510;
var message = “The sum of 5 and 10 is”+ (num1+num2);//The sum of 5 and 10 is 15;

      

减法

    

关系操作符 (>, <, >=, <=)

      

用法说明

        

小于,大于,小于等于,大于等于,这几个操作符用于对两个值进行比较,比较的规则和我们在数学上所学的是一样的,这几个操作符都返回布尔值,例如:var result1 = 5>3; //true   var result2 = 5<3;//false

在ECMAScript中的其他操作符一样,当关系操作符的操作使用了非数值时,也要进行数据转换或完成某些奇怪的操作。以下就是相应的规则。
如果两个操作数都是数值,则执行数值比较。
如果两个操作数都是字符串,则比较两个字符串对应的字符编码值
如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较
如果一个操作数是对象,则调用这个对象的valueOf( )方法,用得到的结果按照前面的规则执行比较。如果对象没有valueOf()方法,则调用toString()方法,并用得到的结果根据前面的规则执行比较。
如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。

关于字符串的比较,有下面两种特殊情况需要注意
1.大写字母的字符编码全部小于小写字母的字符编码,所以会出现下面的情况:
  var result = “Brick”< “alphabet” //true
上面之所以出现,是因为B的字符编码为66,而字母a的字符编码是97。如果要真正按字母表顺序比较字符串,就必须把两个操作数转换为相同的大小写形式(全部大写或全部小写),然后再执行比较,如下所示:

var result = “Brick”.toLowerCase()<“alphabet”.toLowerCase();//false

2.数字字符串的比较,同样比较的同样也是字符串首字母的 字符编码值:

var result = “23”<“3”;  //true
但是
var result = “23”<3;//false
因为规则的第二条,此时会把 字符串 “23”自动转为 23再做比较。

    

相等操作符(==,!=,===,!===)

      

相等== 和 不相等!= 会自动强制类型转换后做比较

        

在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:
如果有一个操作符是布尔值,则在比较相等性之前先将其转换为数值——false转为0,true转为1
如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转为数值;
如果一个操作数是对象,另一个操组数不是,则调用对象的valueOf( )方法,用得到的基本数据类型进行比较
null 和 undefined 是相等的。
要比较相等性之前,不能把null和undefined转为其他任何值
如果有一个操作数是NaN,则相等操作符返回false,不相等操作符返回true。重要提示:即使两个操作数都是NaN,
相等操作符也返回false,因为按照规则NaN不等于NaN
如果两个操作数都是对象,则比较他们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true,否则,返回false.

      

全等===和不全等 !=== 直接做比较,不会类型转换

        

var result1=(“55”==55);//true,因为转换后相等
var result2=(“55”===55);//false,因为不同的数据类型不相等

null == undefined  //true
null === undefined  //false

    

条件操作符(3目表达式)

      

variable = boolean_expression ? true_value :false_value

    

赋值操作符

      

作用:只是简化赋值操作,不会带来任何性能提升

        

var num = 10;
num = num+10;//该行代码可以改为 num+=10;

每个主要算术操作符(以及个别的其他操作符)都有对应的复合赋值操作符。如下:
乘/赋值 (*=)
除/赋值 (/=)
模/赋值 (%=)
加/赋值 (+=)
减/赋值  (-=)
左移/赋值(<<=)
有符号右移/赋值 (>>=)

    

逗号操作符

      

逗号操作符多用于声明多个变量,比如 var num1=1,num2=2,num3=3;

      

逗号操作符用于赋值,在用于赋值时,逗号操作符总会返回表达式中的最后一项,如:var num=(5,1,0);//num结果0

  

语句

    

if语句

      

语法
if(condition) statement1 else statement2  condition条件可以是任意表达式,而且这个表达式的求值不一定是布尔值,ECMAScript会自动调用Boolean()转换函数将这个表达式的结果转换为一个布尔值。如果condition的求值结果是true 则执行statement1,如果condition的求值结果是false,则执行statement2。

      

这两个语句既可以是一行代码,也可以代码块

        

if(i>25)
console.log(“1”); //单行语句可以不使用{}
else if(i=25){
   alert();
  console.log();       //多行语句,必须使用{}括起来。
}else{
  alert();
}
       但是我们推荐,无论单行语句还是多行语句,统统使用{}括起来,这样代码层次更分明。
if(i>25){
console.log(“1”); 
}else if(i=25){
   alert();
  console.log();      }else{
  alert();
}

    

do...while语句

      

定义:后测试循环语句

        

只有在循环体中的代码执行之后,才会测试出口条件。换句话说,对条件表达式求值之前,循环体中代码至少会被执行一次

      

语法

        

do{
           statement
}while(expression)

例如:
var i=0;
do{
  i += 2;
console.log(i);
}while(i<10);  //结果:2,4,6,8,10

    

while语句

      

定义:前测试循环语句

        

也就是说,在循环体内的代码被执行之前,就会对出口条件求值。

      

语法

        

while(expression) 
例如:
var i = 0;
while (i<10){
  i+=2;
}

    

for 语句

      

定义:前测试循环语句

        

它具有在执行循环体之前初始化变量和定义循环后要执行的代码的能力

      

语法:for(initialization;expression,post-loop-expression) statement

        

下面是一个示例:
var count = 10;
for(var i = 0;i < count;i++){
console.log(i);

以上代码定义了变量i的初始值是0 。只有当条件表达式(i<count)返回true的情况下才会进入for循环,因此也有可能不会执行循环体中的代码。如果执行了循环体中的代码,则一定会对循环后的表达式(i++)求值。这个for循环与
     while循环跟那个相同:

var count = 0;
while (i<count){
    console.log(i);
    i++;
}

使用while做不到的,使用for循环同样做不到。也就是说,for循环只是把与循环有关的代码集中在了一个位置。有必要指出的是,在for循环的变量初始化表达式中,也可以不使用var关键字。该变量的初始化可以在外部执行,例如:

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

以上代码与在循环初始化表达式中声明变量的效果是一样的。由于ECMAScript中不存在块级作用域(第4章将进一步讨论这一点),因此在循环内部定义的变量也可以在外部访问到。例如:

var count = 10;
for(var i=0;i<count;i++){
   var a = 100;
}
console.log(i);  // 10
console.log(a);  // 100

        

此外,for语句中的初始化表达式、控制表达式和循环后表达式都是可选的。将这三个表达式全部省略,就会创建一个无限循环,例如:
for(;;){  //无限循环
   doSomething();
}
而只给出控制表达式实际上就把for循环转换成了while循环,例如:

var count = 10;
var i = 0;
for(;i<count;){
  console.log(i);
   i++;
}

    

for-in语句

      

定义: for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性

      

语法:for(property in expression) statement

        

例如:
for(var propName in window){
   document.write(propName);
}

        

上面的例子,每次执行循环,都会将window对象中 存在的一个属性名赋值给变量propName。这个过程会一直持续到对象中的所有属性都被枚举一遍为止。与for语句类似,这里控制语句中的var操作符也不是必需的。但是为了保证使用局部变量,我们推荐上面例子中的做法。

        

ECMAScript对象的属性没有顺序。因此,通过for-in循环输出的属性名的顺序是不可预测的。会因为不同浏览器有差异。
但是,如果表示要迭代的对象的变量值为null或undefined,for-in语句会抛出错误。ECMAScript5更正了这一行为;对这种情况不再抛出错误,而只是不执行循环体。为了保证最大限度兼容性,建议使用for-in循环之前,先检测确认该对象的值不是 null或 undefined.

    

label 语句

      

使用label语句可以在代码中添加标签,以便将来使用。以下是label语句的语法:
label:statement

        

下面是一个示例:

start:for(var i=0;i<count;i++){
    console.log(i);
}
这个例子中定义的start标签可以在将来由break或continue语句引用。加标签的语句一般都要与for语句等循环语句配合使用

    

break 与continue语句

      

break退出整个循环

      

continue退出本次循环,进入下次循环

      

都可以和label配合使用,从而返回代码特定的位置

        

var num = 0;
outermost:
for(var i=0;i<10;i++){
   for(var j=0;j<10;j++){
    if(i==5&&j==5){
break outermost;
}
num++;
}
}
console.log(num);  //55

上文中,如果break不带标签,那么break只能终止 上面例子中内层的循环。结果是95
 现在带了 标签,则就直接终止了整个外层循环。结果是55

        

使用continue的道理是一样的:
var num = 0;
outermost:
for(var i=0;i<10;i++){
   for(var j=0;j<10;j++){
    if(i==5&&j==5){
continue outermost;
}
num++;
}
}
console.log(num);  //95
上文中,如果continue 后不带标签,则只会终止第二层的循环然后进入第二层的下次循环,结果是 99
       现在continue后面带了标签,则会终止第一层的循环,然后进入第一层的下次循环,结果是 95

    

with语句,不推荐使用

    

switch语句

      

与if非常相似,不同点是,switch是===的比较,if是==的比较

      

语法

        

switch (expression){
case value:statement
break;
case value:statement
break;
case value:statement
break;
default:statement
}

switch语句中的每一种情形(case)的含义是:“如果表达式(expression)等于(注意,这里的等于是全等于===)这个值(value),则执行后面的语句(statement)”,而break关键字会导致代码执行流跳出switch语句。如果省略break关键字,就会执行完当前case后,继续执行下一个case。直到遇到一个break跳出,如果后面没有任何break,则将一直执行到最最后的default。最后的default关键字则用于在表达式不匹配前面任何一种情形的时候,执行机动代码(相当于一个else语句)

      

省略break,相当于条件或

        

switch (i){
case 25:
case 35:
console.log(“sdfasfda”);
break;
case 45:
    console.log(“45”);
    break;
default:
    console.log(“Other”);
}
相当于

   if(i==25||i==35){
console.log(“sdfasfda”);
}else if(i==45){
    console.log(“45”);
}else{
    console.log(“Other”);
}

      

我们可以在switch语句中使用任何数据类型,
无论是字符串还是对象都没有问题

        

switch (“hello world”){
        case “hello”+ “world”:
             console.log(“Greeting was found”);
break;
......
}

var num = 25;
switch(true){
  case num < 0:
   console.log(“sfdasf”);
   break;
case num >=10 && num<=10:
   console.log(“fdafdasfaf”);
   break;
}

  

函数

    

基本概念

      

作用:通过函数可以封装多条语句,可以在任意地方调用函数来执行

      

语法:通过function关键字来声明,后跟一组参数以及函数体
function functionName(arg0,arg1......argn){
      statements
}

      

参数:可以有多个参数,之间用逗号隔开,也可以不要参数就只有一个 ()

      

函数调用:通过函数名来调用,后面加上括号,如果有参数,还需要带上参数

      

返回值:函数不需要指定返回值类型。在任何时候,只需要在函数中使用return就可以返回值。如果就只有一个return语句没有带返回的值,则表示返回 undefined,
如果return后面有值,则返回return后面的值,return 后面的语句不再执行

        

function sayHi(){
             return;       //返回undefined
             console.log(“fadfafa”);//不执行
}

function sayHi(){
    return 1;//返回 1
    console.log(“fdafdsfa”);//不执行
}

    

函数参数问题

      

函数不介意传递进来多少参数,也不介意参数类型。也就是说,即便你定义的函数只接收两个参数,在调用时候
你传递一个、三个,甚至不传递参数,解析器都可以解析。因为ECMAScript中的参数在内部是用一个数组来表示的
。函数接收到的始终是一个数组,而不关心数组中包含哪些参数,如果这个数组中不包含任何元素也没问题。实际上,
在函数体内可以通过 arguments对象来访问这个参数数组,比如arguments[0]获取第一个参数,arguments[1]获取第
二个参数等等。

      

arguments对象只是与数组类似(它并不是Array的实例),因为可以使用方括号语法访问它的每一个元素,即arguments[0]获取第
一个,arguments[1]获取第二个
,以此类推。使用length属性来确定实际传进来多少个参数,如果没有参数传递进来,通过
arguments去取的时候,该参数为undefined

        

function say(fir,se){//与fir,se没有任何关系
    console.log("arguments[0]",arguments[0]);
    console.log("arguments[1]",arguments[1]);
   }
   say("a");
   结果: arguments[0] a           arguments[1]  undefined


function say(){
   console.log(arguments.length);
}
say();//0
say(“a”);//1
say(“ss”, “cc”);//2

        

利用 arguments.length这一个功能,来让函数能够接收任意个参数并分别实现适当的功能。
  
   function doAdd(){
       if(arguments.length==1){
console.log(arguments[0]+10);
} else if(arguments.length ==2){
    console.log(arguments[0] + arguments[1] );
}
}


//重写
function doAdd(num1,num2){
if(arguments.length == 1){
    console.log(num1+10);
}else if(arguments.length==2){
    console.log(arguments[0] + num2);
}
}

      

arguments.length的值是与实际传入的参数对应的,如果实际传入1个参数,那么length就等于1。arguments[0] = 1与num1 = 1等价,
但是如果本身就传入了一个参数,通过arguments[1]来赋值,此时不会反应到明明参数中,此时 arguments[1]==undefined

      

没有传递值的命名参数,将自动被赋予undefined

        

function print(a,b){
   console.log(a);// 'hello';
   console.log(b);// undefined
}
print('hello');

      

ECMAScript中传递的都是值,不可能通过引用传递参数。

      

严格模式对如何使用arguments对象作出了一些限制。首先,像前面例子中那样的赋值会变得无效。也就是说,即使把arguments[1]设置为10,num2的值仍然还是undefined。其次重写 arguments的值会导致语法错误

    

函数没有重载(后面的会覆盖前面定义的)

      

function say(num){
return num + 100;
}
function say(num){
 return num + 200;
}
         var result = say(100);//  300

function say(num){
   return num + 100;
}
function say(num,num1){
  return num + 200;
}

var result = say(100);  //300

  

小结

    

ECMAScript中的基本数据类型包括 Undefined、Null、Boolean、String、Number。
引用数据类型:Object、Array、Function。
与其他语言不同,ECMAScript没有为整数和浮点数值分别定义不同的数据类型,Number类型可用于表示所有数值。
ECMASCript中也有一种复杂的数据类型,即Object类型,该类型是这门语言中所有对象的基础类型。
严格模式为这门语言中容易出错的地方施加了限制。
ECMAScript提供了很多与C以及其他类C原因中相同的数据操作符,包括算术操作符、布尔操作符、关系操作符、相等操作符以及赋值操作符等。
ECMAScript从其他语言中借鉴了很多流控制语句,例如if语句、for语句和switch语句等。ECMAScript中的函数与其他语言汇总的函数有诸多不同之处。
无需指定函数的返回值,因为任何ECMAScript函数都可以在任何时候返回任何值。
实际上未指定返回值的函数返回的是一个特殊的undefined值。
ECMAScript中也没有函数签名的概念,因为其函数参数是以一个包含零个或多个值的数组的形式传递的。
可以向ECMAScript函数传递任意数量的参数,并且可以通过arguments对象来访问这些参数。
由于不存在函数签名的特性,ECMAScript函数不能重载。

变量、作用域和内存问题

  

变量

    

变量类型

      

基本类型

        

Undefined

        

Null

        

Boolean

        

Number

        

String

        

Symbol (ES6新增的)

      

引用类型

        

Object

        

Array

        

Date

        

RegExp

        

Function

        

基本包装类型

    

变量的存储

      

基本类型变量的存储

        

存储在栈内存中,不能给基本类型的值添加属性

          

                

引用类型变量的存储

        

引用类型的值是保存在内存中的对象,JavaScript不允许直接访问内存中的位置也就是说
不能直接操作对象的内存空间

        

引用类型的变量由两部分组成。1.引用类型变量的副本,它是一个指针,保存在占内存中,
指向堆内存中的对象
 2.保存在堆内存中的对象

          

              

检测变量类型

      

基本类型的检查

        

基本类型的检测,使用typeof是最好的方式
如果变量是一个对象或null,则typeof会直接返回object

          

如果是String类型,则返回 "string"
如果是Number类型,则返回"number"
如果是Boolean类型,则返回"boolean"
如果是Undefined类型,则返回"undefined"
如果是Null类型,则返回 object
如果是一个对象,则返回object
例如:typeof(1) 结果就是 number

      

引用类型的检测

        

引用类型,使用 instanceof来检测,所有引用类型都属于 Object

          

var a = [];
var person = { name:'张三' }
a instanceof Array    //true
a instanceof Object  //true

person instanceof Object //true

    

复制变量值

      

基本类型值的复制

        

直接使用 = 号即可,会在变量对象上创建一个新值
然后把该值复制到为新变量分配的位置上

var num1 = 5;
var num2 = num1;

          

                

引用类型值的复制

        

浅复制
直接使用 = 号,复制一份指针,指向同一个对象

          

                  

深度复制(两个对象各不影响)

          

let obj = {
   name:'张三',
   message:{
    title:'标题'
   },
   arr:[1,{name:'王'}]
  }
  
  function copyObj(obj){
   let str,newObj = obj.constructor===Array?[]:{};
   
   if(typeof obj!=='object'){
    return;
   }else if(window.JSON){
    str = JSON.stringify(obj);//序列化对象
    newObj = JSON.parse(str); //还原
    console.log("hhhhhhhhhhhhhhhhh");
   }else{
    for(let key in obj){
      newObj[key] = typeof(obj[key])==='object'?copyObj(obj[key]):obj[key];
    } 
    
   }
   
   return newObj;
  }
  
  let object = copyObj(obj);
  obj.message.title = "fafaf";
  obj.arr[1].name='星';
  console.log("obj",obj);
  console.log("object",object);

            

                

传递参数——值传递

      

在向参数传递基本类型的值时,被传递的值会被
复制给一个局部变量
(即命名参数,或者说是arguments中的一个元素)

        

function addTen(num){
   num+=10;
   return num;
}
var count = 20;
var result = addTen(count);
alert(count);//20,没有变化
alert(result);//30

      

传递引用类型的值时,被传递的值仍然会复制一个给局部变量,
只是这个局部变量和原来的变量指向同一个对象

        

函数中的局部变量obj与person都指向同一个对象
function setName(obj){
  obj.name = “Nicholas”;
}
var person = new Object();
setName(person);
alert(person.name); // “Nicholas”

        

局部变量obj一开始与person同时指向一个 对象。
当执行了 obj = new Object() 的时候obj又指向了新的对象,原有的指向并没有去掉。
setName(person)执行完毕后, 指向 new Object()的对象被销毁,这个指针也就没有了。
原来的 指针依然存在。

function setName(obj){
  obj.name = “Nicholas”;
  obj = new Object();
  obj.name = “Greg”;
}
var person = new Object();
setName(person);
alert(person.name); //”Nicholas”

  

执行环境及作用域

    

执行环境

      

执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为

      

执行环境有两种,1是全局执行环境,2是局部(函数)执行环境
全局执行环境是最外围的执行环境,每个函数都有自己的执行环境

      

某个执行环境中的所有代码执行完毕后,该环境被销毁
保存在其中的所有变量和函数定义也将销毁。全局执行环境直到应用退出
——例如关闭网页时才会销毁

      

每个执行环境都有一个与之关联的变量对象

      

全局环境只能访问在全局环境中定义的变量和函数,不能直接访问在局部韩静中的任何数据

      

局部环境除了可以访问函数作用域中的变量,还有权访问其父环境,乃至全局环境

    

变量对象

      

环境中定义的所有变量和函数都保存在这个对象中

    

作用域链

      

当代码在一个环境中执行时,会创建变量对象的一个作用域链

      

作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问

      

作用域链的前端始终都是当前执行的代码所在环境的变量对象

    

延长作用域链

      

try-catch语句的catch块

      

with语句

  

垃圾回收

    

离开作用域的值将被自动标记为可以回收,因此将在垃圾回收期间被删除

    

垃圾回收算法

      

标记清除

      

引用计数

引用类型

  

引用类型剖析(非常重要)

    

var one = {name:"张三"};//第一步
 var two = one;          //第二步
 one = {age:10};         //第三步
 
 console.log(one); //{age:10}
 console.log(two); //{name:"张三"}

      

              

                  

            

Object 类型

    

创建方式

      

构造函数
var person = new Object();

      

字面量创建方式更流行
var person = { name:'张三' }
或者
var person = {}; person.name = '张三';

    

属性访问与赋值方式

      

        

var person = {};  
person.name = '张三';
console.log(person.name);

      

中括号

        

当属性是一个变量或属性中间有空格时候,可以使用  [ ]来访问
var person = {};
person["first name"] = "张";
console.log(person["first name"]);

  

Array 类型

    

特点

      

每一项可以保存任何类型的值

      

数组大小是动态调整的

    

创建方式

      

构造函数
var colors = new Array();
var colors = new Array(20);
var colors = new Array("red","green");

      

字面量
var colors = ["red","blue"];
var name = [];

    

length属性的特点

      

不是只读的,可以用来向数组末尾移除项或向数组中添加新项

        

移除尾部元素
var colors = ["red","blue","green"];
colors.length = 2;
alert(colors[2]);  //undefined

        

如果length属性设置为大于数组项数的值,
则新增的每一项都会取得undefined值

var colors = ["red","blue","green"];
colors.length = 4;
alert(colors[3]);  //undefiined

        

var colors = ["red","blue","green"];
colors[colors.length] = "black";//在位置3添加一种颜色
colors[colors.length] = "brown";//在位置4添加一种颜色

var colors = ["red","blue","green"];
colors[99] = "black";
alert(colors.length);//100

    

检测数组

      

if(value instance of Array){
}

      

//这种方法有兼容性问题
if(Array.isArray(value)){

}

    

转换方法

      

toString()方法,返回以逗号分割的字符串,例如:var colors = ["red","blue"];  colors.toString();// red,blue

      

join()方法,返回以指定字符分割的字符串,例如:var colors = ["red","blue"]; colors.join("#");//  red#blue

    

栈方法与队列方法

      

push(e1,e2,,,en) ,向数组末尾添加1个或多个元素

        

var arr = ['a','b','c'];
 arr.push('d','e');
 console.log(arr);// ["a", "b", "c", "d", "e"]

      

pop(),获取最后一个元素,并去除最后一个元素

        

var arr = ['a','b','c'];
 var item = arr.pop();
 console.log(item);// c
 console.log(arr);// ["a", "b"]

      

unshift(e1,e2,,,,en),向数组头部添加n个元素,并返回数组总长度

        

var arr = ['a','b','c'];
 var count = arr.unshift('d','e');
 console.log(count);//
 console.log(arr);//

      

shift(),返回数组第一个元素并将数据第一个元素移除

        

var arr = ['a','b','c'];
 var item = arr.shift();
 console.log(item);//a
 console.log(arr);// ['b','c'];

    

重排序方法

      

reverse(),反转数组项的顺序

        

var arr = ['a','b','c'];
 arr.reverse();
 console.log(arr); // ["c", "b", "a"]

      

sort()

        

默认按照字符串升序排序,内部会调用toString()方法转型,如果是数值型可能不能满足条件

          

var arr = [0,1,5,10,15];
 arr.sort();
 console.log(arr); // [0, 1, 10, 15, 5]

        

传入比较函数,实现排序(从小到大或从大到小)

          

var arr = [0,1,5,10,15];
 
 function compare(value1,value2){ //从大到小
  if(value2<value1){
   return -1;
  }else if(value2>value1){
   return 1;
  }else{
   return 0;
  }
 }
 
 arr.sort(compare);
 console.log(arr);//[15, 10, 5, 1, 0]
 
 //或者
 
 var arr1 = [0,1,5,10,15];
 
 function compare1(value1,value2){//从大到小
  return value2 - value1;
 }
 
 arr1.sort(compare1);
 console.log(arr1);////[15, 10, 5, 1, 0]

          

var arr = [0,5,1,10,15];
 
 function compare(value1,value2){ //从小到大
  if(value2<value1){
   return 1;
  }else if(value2>value1){
   return -1;
  }else{
   return 0;
  }
 }
 
 arr.sort(compare);
 console.log(arr);// [0, 1, 5, 10, 15]
 
 //或者
 
 var arr1 = [0,1,5,10,15];
 
 function compare1(value1,value2){//从小到大
  return value1 - value2;
 }
 
 arr1.sort(compare1);
 console.log(arr1);// [0, 1, 5, 10, 15]

    

操作方法 

      

concat() 添加元素生成新数组

        

无参时,浅复制了数组

          

var arr = ['a',{name:'张三'}];
  
  var arrCopy = arr.concat();
  arr[1].name = '李四';
  
  console.log("arrCopy",arrCopy);//['a',{name:'李四'}];
  
  console.log("arr",arr);//['a',{name:'李四'}];
  
  console.log(arr===arrCopy);//false

        

如果传入一个或多个数组,则将这些数组中国的每一项都添加到结果数组中,形成一个新的数组

          

var arr = ['a','b','c'];
 var arr1 =['d','e'];
 var arr2 = ['f'];
 
 var arrN =  arr.concat(arr1,arr2);
 console.log(arr);//["a","b","c"];
 console.log(arrN);//["a","b","c","d","e","f"];

        

如果传递的不是数组,这些值就会被简单的添加到结果数组的末尾,形成一个新的数组

          

var a = ['a'];
 var a1 = 'b';
 var a2 = 'c';
 
 var aN = a.concat(a1,a2);
 console.log(aN);//["a","b","c"];

      

slice() 获取原来数组中的部分元素形成新数组(浅复制)
这里的浅复制,也就是说,如果数组的元素是引用类型的
那么通过slice()生成的数组中国的元素执行同一块内存地址,
修改其中一个,另一个数组中的元素也会随之改变

        

没有参数时,则是复制原来数组中的所有元素形成一个新数组

        

一个参数时,获取从当前位置到数组结尾的所有项  slice(index)

        

两个参数时候,从当前位置到第二个参数的位置,第二个参数的位置不包括在内(包头不包尾) slice(begin,end)

      

splice() 删除,插入,替换  splice方法会改变原数组,
并且返回删除的项形成的数组,如果删除项为0,则返回空数组

        

删除 arr.splice(index.count),从index位置开始,删除count个元素

          

var arr = ['a','b','c'];
 arr.splice(1,1);//从下标1开始,删除1个元素
 console.log(arr);// ["a","c"];

        

插入 arr.splice(index,0,e1,e2,en) 从index位置开始,删除0个元素,然后再index的位置添加元素e1,e2....en

          

var arr = ['a','b','c'];
 arr.splice(1,0,'张三','李四');//从下标1开始,删除0个元素,在下标1的位置添加 张三和李四
 console.log(arr);// ["a","张三","李四","b","c"];

        

替换 arr.splice(index.count,e1,e2,en) 从index位置开始,删除count个元素,然后在idex的位置添加元素e1,e2,,,,en

          

var arr = ['a','b','c'];
 arr.splice(1,1,'张三','李四');//从下标1开始,删除1个元素,在下标1的位置添加 张三和李四
 console.log(arr);// ["a","张三","李四","c"];

    

位置方法

      

arr.indexOf(element,beginIndex) 
查找某元素在数组中第一次出现的索引位置,如果没找到则返回 -1
第二个参数可选,表示开始查找的位置

      

arr.lastIndexOf(element,beginIndex) 
查找某元素在数组中第一次出现的索引位置(从后往前查找),如果没找到则返回 -1
第二个参数可选,表示开始查找的位置

      

注意:第一个参数比较时候,是使用的全等===

        

var numbers = [1,2,3,4,5,4,3,2,1];
 
 console.log(numbers.indexOf(4)); // 3
 console.log(numbers.lastIndexOf(4)); //5
 console.log(numbers.indexOf(4,4));//5
 console.log(numbers.lastIndexOf(4,4));//3
 
 var person = {name:"张三"};
 var people = [{name:"张三"}];
 
 var morePeople = [person];
 
 console.log(people.indexOf(person));  //-1
 console.log(morePeople.indexOf(person));//0

      

浏览器支持:IE9+ 、Firefox2+、Safari3+、Opera9.5+和Chrome

    

迭代方法

      

every()

        

有返回值,对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true

      

filter()

        

有返回值,对数组中每一项运行给定函数,返回该函数会返回true的项组成的数组

      

forEach()

        

无返回值,对数组中的每一项运行给定函数

          

var arr = [1,2,3,4,5,4,3,2,1];
 
 var everyResult = arr.every(function(item,index,array){
  return item>0;
 })
 
 var filterResult = arr.filter(function(item,index,array){
  return item>2;
 })
 
 console.log(everyResult);//true
 console.log(filterResult);//[3, 4, 5, 4, 3]
 
 arr.forEach(function(item,index,array){
  if(item>4){
   console.log(item);//5
  }
 })
 
 var mapResult = arr.map(function(item,index,array){
  return item*2;
 })
 
 console.log(mapResult);//[2,4,6,8,10,8,6,4,2];
 
 var someResult = arr.some(function(item,index,array){
  return item==5;
 })
 console.log(someResult);  //true

      

map()

        

有返回值,对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组

      

some()

        

有返回值,对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true

      

说明:以上五个方法都有一个参数,那就是要在每一项上运行的函数,
该函数都有三个参数,分别是item当前项,index当前项在数组中的位置,array该数组本身
以上方法都不会修改原数组
浏览器支持——IE9+,Firefox2+、Safari 3+、Opera 9.5+和Chrome

    

归并方法(求数组的sum)

      

arr.reduce(function([rev,cur,index,array){
})
rev是上一个值,cur是当前值

        

//使用reduce计算数组的和
 var arr = [1,2,3];
 var sum = arr.reduce(function(prev,cur){
  return prev + cur;
 })
 console.log(sum);// 6 

      

arr.reduceRight(function([rev,cur,index,array){
})
rev是上一个值,cur是当前值
与reduce的唯一区别是,reduce是从数组第一项开始遍历所有项,
而reduceRight是从数组的最后一项开始

        

//使用reduceRight计算数组的和
 var arr = [1,2,3];
 var sum1 = arr.reduceRight(function(prev,cur){
  return prev + cur;
 })
 console.log(sum);// 6

  

Date 类型

    

暂时跳过

  

RegExp 类型

    

暂时跳过

  

Function 类型

    

没有重载(深入理解)

      

因为函数的创建也可以是函数表达式的方式,结果就是后面的函数覆盖了前面的函数

        

var addSomeNumber = function(num){
  return num + 100;
 }
 var addSomeNumber = function(num){
  return num + 200;
 }

    

函数声明与函数表达式

      

是什么

        

函数声明
function addNumber(num){
   return num +100;
}

        

函数表达式
var addNumber =  function(num){
   return num +100;
}

      

区别

        

函数声明存在提升,但是函数表达式不存在提升。
函数表达式,必须等到解析器执行到它所在的代码行,才会真正被解析执行

          

函数表达式不存在提升,所以报错
console.log(addSomeNumber(5));//Uncaught TypeError: addSomeNumber is not a function
 
 var addSomeNumber = function(num){
  return num + 100;
 }

          

函数声明具有提升
console.log(addSomeNumber(5));//105
 
 function addSomeNumber(num){
  return num + 100;
 }

          

因为提升,所以最终执行的都是 +20的函数
var addNumber = function(num){
  return num + 20;
 }
 
 function addNumber(num){
  return num + 10;
 }
 
 console.log(addNumber(10));//30

          

function addNumber(num){
  return num + 10;
 }

 var addNumber = function(num){
  return num + 20;
 }
 
 console.log(addNumber(10));//30

    

将函数作为参数传递(必须去掉函数后面的括号)

      

function callSomeFunction(someFunction,functionArgs){
  return someFunction(functionArgs);
 }
 
 function add10(num){
  return num + 10;
 }
 
 var result1 = callSomeFunction(add10,10);
 console.log(result1);// 20
 
 function getGreeting(name){
  return "Hello, " + name;
 }
 
 var result2 = callSomeFunction(getGreeting,"张三");
 console.log(result2);// Hello, 张三

    

函数内部对象

      

函数的arguments对象在 基本概念这一章的 函数 部分提到

      

this对象——this引用的是函数执行的环境对象

        

var color = "red";
 
 var o = {color:"blue"};
 function sayColor(){
  console.log(this.color);
 }
 
 sayColor();//"red"
 
 o.sayColor = sayColor;
 o.sayColor();//"blue"

    

函数内部属性

      

arguments的callee属性,该属性是一个指针,指向拥有这个arguments对象的函数。记住函数就是一个引用类型的变量
ECMAScript中的函数是对象,当函数在严格模式下运行时,访问 arguments.callee会报错

        

//内存分析与 本章节的  “引用类型剖析(非常重要)”一样,注意分析
 function factorial(num){
  if(num<=1){
   return 1;
  }else{
   return num * factorial(num-1);//这种方式耦合度很高,在本例中的结果出人意外
  }
 }
 
 var trueFactorial = factorial;
 
 factorial = function(){
  return 0;
 }
 console.log(trueFactorial(5));//0
 console.log(factorial(5));//0

        

//内存分析与 本章节的  “引用类型剖析(非常重要)”一样,注意分析
 function factorial(num){
  if(num<=1){
   return 1;
  }else{
   return num * arguments.callee(num-1);
  }
 }
 
 var trueFactorial = factorial;
 
 factorial = function(){
  return 0;
 }
 console.log(trueFactorial(5));//120
 console.log(factorial(5));//0

    

函数属性和方法

      

属性

        

length

          

函数的length属性表示函数希望接收的命名参数的个数,而arguments的length属性表示实际接收的参数的个数
function  addNumber(num){
  console.log(arguments.length);
  return num + 10;
 }
 var nums = addNumber();
 console.log(addNumber.length);//1,此时 arguments.length = 0
 
 var nums1 = addNumber(100);
 console.log(addNumber.length);//1,此时 arguments.length = 1

        

prototype

          

对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在
。也就是说,诸如toString()和valueOf()等方法实际上都保存在prototype名下,只不过
是通过各自对象的实例访问罢了。在创建自定义引用类型以及实现继承时,prototype属性的作用
极其重要。prototype属性是不可枚举的,因此使用 for-in无法发现

      

方法(自身所拥有的,非继承而来的)

        

apply

          

用法

            

接收两个参数,第一个参数是在其中运行函数的作用域
第二个参数是Array实例,也可以是arguments对象。第二个参数用来
传递函数的参数,如果不传递参数,则第二个参数可以省略

              

var o = {num:10};
 var num = 100;
 
 function sum(num1,num2){
  return num1 + num2 + this.num;
 }
 
 function callSum1(num1,num2){
  return sum.apply(o,arguments);
 }
 
 function callSum2(num1,num2){
  return sum.apply(this,arguments); //这里到的this实际上就是window
 }
 
 function callSum3(num1,num2){
  return sum.apply(window,arguments); //这里到的this实际上就是window
 }
 
 function callSum4(num1,num2){
  return sum.apply(this,[num1,num2]); //数组传参
 }
 
 console.log(callSum1(10,10));//30
 console.log(callSum2(10,10));//120
 console.log(callSum3(10,10));//120
 console.log(callSum4(10,10));//120

          

作用

            

1.传递参数
2.扩充函数赖以运行的作用域

        

call

          

用法

            

与apply的用法很相似,只是传递函数的参数需要一个个传入,
而不像apply那样传递一个
数组或arguments。call与apply的结果没有什么不同

              

var o = {num:10};
 var num = 100;
 
 function sum(num1,num2){
  return num1 + num2 + this.num;
 }
 
 function callSum1(num1,num2){
  return sum.call(o,num1,num2);
 }
 
 function callSum2(num1,num2){
  return sum.call(this,num1,num2);
 }
 
 
 function callSum3(num1,num2){
  return sum.call(this,num1,num2); 
 }
 
 console.log(callSum1(10,10));//30
 console.log(callSum2(10,10));//120
 console.log(callSum3(10,10));//120

          

作用

            

1.传递参数
2.扩充函数赖以运行的作用域

        

bind

          

用法

            

只有一个参数,该参数是一个对象

          

作用

            

扩充函数赖以运行的作用域,bind只负责绑定运行的作用域,不会直接就执行函数了

              

var o = {num:10};
 var num = 100;
 
 function sum(){
  return this.num*2;
 }
 
 function callSum1(){
  return sum.bind(o)();
 }
 
 function callSum2(){
  return sum.bind(this)();
 }
 
 
 
 
 console.log(callSum1());//20
 console.log(callSum2());//200

          

浏览器兼容性

            

IE9+、Firefox4+、Safari5.1+、Opera12+和Chrome

  

基本包装类型

    

Boolean、Number、String共性

      

Boolean、Number、String是3个特殊的引用类型。与其它引用类型有相似,但同时有各自特殊的行为

      

var s1 = "some text";
var s2 = s1.substring(2);
实质上后台自动帮我们完成了下面操作
var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;
引用类型与基本包装类型的主要区别就是 对象的声明周期。使用new操作符创建的引用类型的实例,
在执行流离开当前作用域之前都一直保存在内存中,而自动创建的基本包装类型的对象,则只存在于
一行代码的执行瞬间,然后立即被销毁。这意味着我们不能在运行时为基本类型值添加属性和方法

        

var s1 = "some";
s1.color = "red";
console.log(s1.color);//undefined
var s2 = "some";
s2.color = "blue";
console.log(s2.color);//blue

      

var obj = new Object("aa");  
console.log(obj instanceof String);//true

使用new调用基本类型的构造函数,与直接调用同名的转型函数是不一样的
var value = "25";
var number = NUmber(value);//转型函数
console.log(typeof number) ; ///"number"
对基本包装类型调用typeof都会返回 object
var a = new String("a");
typeof a // "object"


    

Boolean

    

Number

    

String

      

字符方法

        

charAt();获取某个位置的字符

          

var a = "hello world";
console.log(a.charAt(1));//"e"

      

字符串操作方法

        

concat(e1,e2...,en);拼接1个或多个参数,原来的值不变

          

var a = "a";
var b = a.concat("b","c","d");
console.log(a);//"a"
console.log(b);//"abcd"

        

slice、substring、substr
他们也不会改变原来的值,都可以传入2个参数,。
只是 substr的第二个参数时指返回字符的个数,而slice和substring的第二个参数是指返回的下标index

          

var temp = "hello world";
 console.log(temp.slice(3));//lo world
 console.log(temp.substr(3));//lo world
 console.log(temp.substring(3));//lo world
 
 console.log(temp.slice(3,7));//lo w
 console.log(temp.substr(3,7));//lo worl
 console.log(temp.substring(3,7));//lo w

      

字符串位置方法

        

indexOf()  返回某字符所在的位置

          

var str = "hello world"; console.log(str.indexOf("0"));//4

        

lastIndexOf() 返回某字符最后一次所在的

          

var str = "hello world"; console.log(str.lastIndexOf("0"));//7

      

trim()去除前面和后面的空格、trimLeft()去除左空格、trimRight()去除右空格

        

var str = "  hello  ";
console.log(str.trim());//"hello"
console.log(str.trimLeft());//"hello  "
console.log(str.trimRight());//"  hello"

      

toLowerCase()全部转为小写
toUpperCase()全部转为大写

        

var str = "hellO";
console.log(str.toLowerCase());//"hello"
console.log(str.toUpperCase());//HELLO

      

替换方法 replace

        

str.replace(oldChar,newChar)

          

var str = "cat bat";
 var result = str.replace("a","z");
 console.log(result);//czt bat

        

使用正则表达式,指定全局g标志,可以替换所有字符
str.replacee(regx,newChar)

          

var str = "cat bat";
 var result = str.replace(/a/g,"z");
 console.log(result);//czt bzt

    

单体内置对象(这两个不需要实例化)

      

Global对象

        

URI编码

          

encodeURI

            

不会对本身属于URI的特殊字符进行编码,例如冒号、正斜杠、问号和井号

              

var url = "https://www.baidu.com/illegal value.html#start";
 var result = encodeURI(url);
 
 var result1 = encodeURIComponent(url);
 
 console.log(result);//  https://www.baidu.com/illegal%20value.html#start
 console.log(result1);// https%3A%2F%2Fwww.baidu.com%2Fillegal%20value.html%23start

          

encodeURIComponent

            

会对它发现的任何非标准字符进行编码。(所有非 字母数字)
一般来说,我们使用encodeURIComponent()方法的时候要比使用encodeURI()更多,因为
在实践中更常见的是对查询字符串参数而不是对基础URI进行编码。
不要使用escape()和unescapde()方法

        

URI解码

          

decodeURI

            

decodeURI只能解码 encodeURI的

          

decodeURIComponent

            

decodeURIComponent只能解码 encodeURIComponent的

        

eval方法

          

eval()接收一个参数,那就是变量字符串或函数字符串,在eval()中创建的任何变量或函数都不会被提升。
eval()方法也是很危险的, 要注意 代码注入

            

eval("console.log('1')");//1

        

window对象

          

ECMAScript 虽然没有住处如何直接访问Global对象,但Web浏览器都是将这个全局对象作为window对象的一部分
加以实现的。因此,在全局作用域中声明的 所有变量和函数,就都成为了widow对象的属性。

            

var color = "red";
function sayColor(){
  console.log(window.color)
}
window.sayColor();//"red"

      

Math对象

        

Math对象的属性

        

min()和max()方法,用于确定一组数值中的最小值和最大值

          

var max = Math.max(3,64,21,16);//54
var min = Math.min(3,54,32,16);//3

          

var values = [1,2,3,4,5,6,7,8];
var max = Math.max.apply(Math,values);//8
var min = Math.min.apply(Math.values);//1
这个技巧的关键是把Math对象作为apply()的第一个参数,从而正确地设置this值
然后,可以将任何数组作为第二个参数。

        

舍入方法

          

Math.ceil()向上取整

          

Math.floor()向下取整

            

Math.ceil(25.9);//26
Math.ceil(25.5);//26
Math.ceil(25.1);//26

Math.round(25.9);//26
Math.round(25.5);//26
Math.round(25.1);//25

Math.floor(25.9);//25
Math.floor(25.5);//25
Math.floor(25.1);//25

          

Math.round()四舍五入取整

        

random()方法,返回大于等于0小于1的随机数

          

值 = Math.floor(Math.random()* 可能值的总数 + 第一个可能的值)

            

1到10之间的整数(包括1和10)
var num = Math.floor(Math.random()*10+1)
2到10之间的整数(包括2和10)
var num = Math.floor(Math.random()*9+2)

        

其他方法

面向对象的程序设计

  

 理解对象

    

对象是什么

      

对象是无序属性的集合,其属性值可以包含基本值、对象或者函数。对象无非就是一组名值对,其值可以是数据或函数

        

创建自定义最简单的方式,创建一个
Object的实例,然后为它添加属性和方法

          

var person = new Object();
 person.name = "张三";
 person.sayName = function(){
  console.log(this.name);
 }
 
 person.sayName();//张三

        

对象字面量创建对象是首选模式

          

var person = {
    name:"张三",
    sayName:function(){
     console.log(this.name);
    }
   }
   
   person.sayName();//张三

    

JavaScript是如何来定义对象的属性和方法的呢

      

数据属性

      

访问器属性

  

创建对象的模式

    

虽然上面提到了可以通过创建一个Object实例的方式或者字面量的方式创建对象,
但是,这种创建对象的方式会产生大量的重复代码,所以提出了下面几种创建模式

    

工厂模式

      

内容

        

function createPerson(name,age,job){
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function(){
   console.log(this.name);
  }
  return o;
 }
 
 var person1 = createPerson("Nicholas",29,"Software Engineer");
 var person2 = createPerson("Greg",27,"Doctor");

      

缺点

        

没有解决对象标识的问题(怎样知道一个对象的类型)

    

构造函数模式

      

内容

        

function Person(name,job,age){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
     console.log(this.name);
    }
   }
   
   var person1 = new Person("张三","fdasf",20);
   var person2 = new Person("李四","fdsafdffgedhr",18);
   
   console.log(person1 instanceof Person);//true
   console.log(person2 instanceof Person);//true

      

可以当做普通函数使用

        

function Person(name,job,age){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
     console.log(this.name);
    }
   }
   
   //作为普通函数使用
  Person("张三","engineer",20);// 添加到window
  window.sayName();//张三
  
  //在另一个对象的作用域中调用
  var o = new Object();
  Person.call(o,"李四",18);
  o.sayName();//李四

      

缺点

        

每个方法都要在每个实例上重新创建一遍

    

原型模式

      

图解原型对象

        

                  

function Person(){
     
    }
    
    Person.prototype.name = "张三";
    Person.prototype.job = "java";
    Person.prototype.arr = [1,2,3];
    Person.prototype.arr2 = [9,10];
    
    Person.prototype.sayName = function(){
     console.log(this.name);
    }
    
    var p1 = new Person();
    p1.sayName();//"张三"
    console.log(p1.arr);// [1,2,3];
    
    p1.job = "javaEE";
    console.log(p1.job);//"javaEE"
    p1.arr2.push(11);
    console.log(p1.arr2);//[9,10,11]
    
    var p2 = new Person();
    p2.sayName();//"张三"
    console.log(p2.arr);//[1,2,3]
    console.log(p2.job);//"java"
    console.log(p2.arr2);//[9,10,11]

      

内容

        

无论什么时候,只要创建了一个新函数,该函数就会创建一个prototype(原型)属性,它是一个指针,
指向该函数的一个对象(我们称之为原型对象)原型对象包含可以有该函数实例化出来的所有实例共享
的属性和方法。默认情况下,原型对象都会自动获得一个constructor(构造函数)属性,这个属性指向
prototype所在的函数。原型对象中的其他属性和方法都是从Object继承而来的。
当调用构造函数创建一个新实例后,该实例会包含一个内部属性,它是一个指针,指向构造函数的原型对象。
这里强调一点,原型对象中的属性和方法被由该构造函数的所有实例共享。对于原型对象中的基本类型的值,
所有实例在创建时,都会得到该值的一个副本拷贝。当实例修改这个基本类型值时,各个实例之间互不影响。
当原型对象中是引用类型的值时,所有实例是以指针的方式共享该引用类型的值。当某一个实例更改了该值,
其他实例指向的该值会收到影响。


        

通过下面两种方式,也可以判断对象的类型

          

    console.log( Person.prototype.isPrototypeOf(p1) );//true
    console.log( Person.prototype.isPrototypeOf(p2) );//true
    console.log( Object.prototype.isPrototypeOf(p1) );//true  //因为所有的对象都继承Object
    console.log( Object.prototype.isPrototypeOf(p1) );//true  //因为所有的对象都继承Object
    
    console.log(Object.getPrototypeOf(p1)==Person.prototype);//true  // getPrototypeOf是 ES5新增的,获取对象自身所属的prototype。
    console.log(Object.getPrototypeOf(p2)==Person.prototype);//true
    console.log(Object.getPrototypeOf(p1)==Object.prototype);//false
    console.log(Object.getPrototypeOf(p2)==Object.prototype);//false

        

属性是如何访问的

          

当代码读取某个对象的某个属性时,先查找对象实例自身有没有该属性,如果有,就返回该属性的值。如果没找到,
就继续找对象所指向的原型对象中有没有该属性,如果有,则返回该属性的值。如果没找到,继续找父级对象实例
中有没有该属性,如果有,则返回该属性值。如果没有找到,继续找父级实例对象对应的原型对象中有没有,找到了
则返回属性的值,如果没找到,继续找父级的父级,先找父级的父级的实例对象,再找父级的父级的原型对象。
就这样一直找到Object对象为止。这也就是 原型链。原型链也正是实现继承的主要方法。

        

实例中的属性只会屏蔽原型中的同名属性,但不会覆盖

          

function Person(){
     
    }
    
    Person.prototype.name = "张三";
    Person.prototype.job = "java";
    Person.prototype.arr = [1,2,3];
    Person.prototype.arr2 = [9,10];
    
    Person.prototype.sayName = function(){
     console.log(this.name);
    }
    
    var p1 = new Person();
    p1.name = "李四";
    console.log(p1.name);"李四"   //来自实例
    
    var p2 = new Person();
    console.log(p2.name);"张三"  //来自原型

        

delete方法会删除实例属性

          

function Person(){
     
    }
    
    Person.prototype.name = "张三";
    Person.prototype.job = "java";
    Person.prototype.arr = [1,2,3];
    Person.prototype.arr2 = [9,10];
    
    Person.prototype.sayName = function(){
     console.log(this.name);
    }
    
    var p1 = new Person();
    p1.name = "李四";
    console.log(p1.name);"李四"   //来自实例
    
    var p2 = new Person();
    console.log(p2.name);"张三"  //来自原型
   delete p1.name;
console.log(p1.name); //"张三" //来自原型

        

hasOwnProperty()检测属性是否存在实例中

          

function Person(){
     
    }
    
    Person.prototype.name = "张三";
    Person.prototype.job = "java";
    Person.prototype.arr = [1,2,3];
    Person.prototype.arr2 = [9,10];
    
    Person.prototype.sayName = function(){
     console.log(this.name);
    }
    
    var p1 = new Person();
    p1.name = "李四";
    console.log(p1.name);"李四"   //来自实例
    
    console.log(p1.hasOwnProperty("name"));//true
    
    var p2 = new Person();
    console.log(p2.name);"张三"  //来自原型
    
    console.log(p2.hasOwnProperty("name"));//false
    
    delete p1.name;
    console.log(p1.name);//"张三" //来自原型
    console.log(p1.hasOwnProperty("name"));//false

        

原型与in操作符

          

单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论属性存在于实例中还是原型中

            

function Person(){
     
    }
    
    Person.prototype.name = "张三";
    Person.prototype.job = "java";
    Person.prototype.arr = [1,2,3];
    Person.prototype.arr2 = [9,10];
    
    Person.prototype.sayName = function(){
     console.log(this.name);
    }
    
    var p1 = new Person();
    p1.name = "李四";
    console.log(p1.name);"李四"   //来自实例
    
    console.log(p1.hasOwnProperty("name"));//true
    console.log("name" in p1);//true
    
    var p2 = new Person();
    console.log(p2.name);"张三"  //来自原型
    
    console.log(p2.hasOwnProperty("name"));//false
    console.log("name" in p2);//true
    
    delete p1.name;
    console.log(p1.name);//"张三" //来自原型
    console.log(p1.hasOwnProperty("name"));//false
    console.log("name" in p1);//true

          

定义一个函数,来判断属性是否存在于 原型中

            

function hasPrototypeProperty(object,name){
     return !object.hasOwnProperty(name)&&(name in object);
    }
    
    console.log(hasPrototypeProperty(p1,"name"));//true

          

使用 for in循环,也可以访问到可枚举的属性,无论属性存在实例中还是原型中

            

function Person(){
     
    }
    
    Person.prototype.name = "张三";
    Person.prototype.job = "java";
    Person.prototype.arr = [1,2,3];
    Person.prototype.arr2 = [9,10];
    
    Person.prototype.sayName = function(){
     console.log(this.name);
    }
    
    var p1 = new Person();
    p1.name = "李四";
    console.log(p1.name);"李四"   //来自实例
    
    console.log(p1.hasOwnProperty("name"));//true
    console.log("name" in p1);//true
    
    var p2 = new Person();
    console.log(p2.name);"张三"  //来自原型
    
    console.log(p2.hasOwnProperty("name"));//false
    console.log("name" in p2);//true
    
  for(var prop in p1){
   console.log(prop);// name job arr arr2 sayName
  }
  
  for(var prop in p2){
   console.log(prop);// name job arr arr2 sayName
  }

          

获取对象上所有可枚举的实例属性的字符串数组,使用object.keys(obj)

            

function Person(){
     
    }
    
    Person.prototype.name = "张三";
    Person.prototype.job = "java";
    Person.prototype.arr = [1,2,3];
    Person.prototype.arr2 = [9,10];
    
    Person.prototype.sayName = function(){
     console.log(this.name);
    }
    
    var p1 = new Person();
    p1.name = "李四";
    console.log(p1.name);"李四"   //来自实例
    
    console.log(p1.hasOwnProperty("name"));//true
    console.log("name" in p1);//true
    
    var p2 = new Person();
    console.log(p2.name);"张三"  //来自原型
    
    console.log(p2.hasOwnProperty("name"));//false
    console.log("name" in p2);//true
    
  for(var prop in p1){
   console.log(prop);// name job arr arr2 sayName
  }
  
  for(var prop in p2){
   console.log(prop);// name job arr arr2 sayName
  }
  console.log(Object.keys(p1));// ["name"]
  console.log(Object.keys(p2));// []

        

更简单的原型语法

          

function Person(){
     
    }
    
    Person.prototype = {
     name:"张三",
     job:"java",
     sayName:function(){
      console.log(this.name);
     }
    }
    
    //上面就是用字面量重写原型对象,但是会导致 原型对象的constructor属性指向Object构造函数,而不会指向Person函数了
    //尽管通过 instanceof操作符还能返回正确的结果,但是通过constructor已经不能确定对象类型了。
      var friend = new Person();
   console.log(friend instanceof Object);//true
   console.log(friend instanceof Person);//true
   console.log(friend.constructor == Person);//false
   console.log(friend.constructor == Object);//true
   
   //如果constructor的值真的很重要,可以像下面一样特意将它设置回适当的值。
   Person.prototype = {
         constructor:Person,
        name:"张三",
        job:"java",
        sayName:function(){
         console.log(this.name);
        }
   }

        

原型的动态性

          

由于原型中查找值的过程是一次搜索,因此我们对原型对象所做的修改都能立即从实例上反映出来
——即使先创建了实例后修改原型也照样如此。

            

function Person(){
     
    }
    
    var friend = new Person();
    Person.prototype.sayHi = function(){
     console.log("hi");
    }
    friend.sayHi(); // "hi"

          

但是如果重写整个原型对象,那么情况就不一样了。我们知道,调用构造函数时会为实例
添加一个指向最初原型的[[ Prototype ]]指针,而把原型修改为另外一个对象就等于切断了
构造函数与最初原型之间的联系。记住:实例中的指针仅指向原型,而不指向构造函数

            

function Person(){
     
    }
    
    var friend = new Person();
    Person.prototype = {
     constructor:Person,
     name:'张三',
     sayName:function(){
      console.log(this.name);
     }
    }
    friend.sayName(); // error

              

                      

原生对象的原型

          

原型模式的重要性不仅体现在自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。

          

所有原生引用类型(Object、Array、String等等)都在其构造函数的原型上定义了方法。
例如,Array.prototype中可以找到sort()方法,在String.prototype中可以找到subString()方法

            

console.log(typeof Array.prototype.sort);//"function"
console.log(typeof String.prototype.substring);//"function"

          

通过原生对象的原型不仅可以获取所有默认方法的引用,而且可以定义新方法,可以像修改自定义对象
的原型一样修改原生对象的原型。为String添加一个名为 startsWith()的方法

            

String.prototype.startsWith = function(text){
  return this.indexOf(text) == 0;
 }
 
 var msg = "Hello world!";
 //应为msg是字符串,而且后台会自动调用String基本包装函数创建这个字符串,
 //因此通过msg就可以调用startsWith()方法
    console.log(msg.startsWith("He"));//true

        

原型对象的问题

          

因为它的共享属性,对原型对象中的引用类型的值来说,一个实例修改了该值,其他实例的值也就变了,
所以原型模式,很少单独使用

    

组合使用构造函数模式和原型模式

      

内容

        

使用构造函数模式定义实例属性,而原型模式定义方法和共享的属性

          

function Person(name,age,job){
  this.name = name;
  this.age = age;
  this.job = job;
  this.friends = ['tom','jack'];
 }
 
 Person.prototype = {
  constructor:Person,
  sayName:function(){
   console.log(this.name);
  }
 }
 
 var person1 = new Person('张三',20,'java');
 var person2 = new Person('李四',18,'doctor');
 
 person1.friends.push('Bob');
 
 console.log(person1.friends);//['tom','jack','Bob'];
 console.log(person2.friends);//['tom','jack'];
 console.log(person1.friends == person2.friends);//false 
 console.log(person1.sayName == person2.sayName);//true

      

好处

        

这样可以使 每个实例都会有自己的一份实例属性的副本,但同时又共享这对方法的应用,最大限度地节省了内存。
这种混成模式还支持想构造函数传参,可谓集两种模式之长。
这种模式是目前使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认
模式

    

动态原型模式

      

内容(基本上与组合使用构造函数模式和原型模式是一样的,只是在构造函数中动态的添加原型对象的属性与方法而已)
注意:在动态原型模式使用时,不能使用字面量重写原型。前面已经提过了,如果在已经创建了实例的情况下重写原型,
就会切断现有实例与新原型之间的联系

        

function Person(name,age,job){
  this.name = name;
  this.age = age;
  this.job = job;
  this.friends = ['tom','jack'];
  if(typeof this.sayName != "function"){
   Person.prototype.sayName = function(){
    console.log(this.name);
   }
  }
 }

 var person1 = new Person('张三',20,'java');
 person1.sayName(); //张三

    

寄生构造函数模式

    

稳妥构造函数模式

  

继承   (实现继承的方式)

    

原型链

      

什么是原型链

        

说到原型链,就要先知道什么是原型
无论什么时候,只要创建了一个新函数,该函数就会创建一个prototype(原型)属性,它是一个指针,
指向该函数的一个对象(我们称之为原型对象)原型对象包含可以有该函数实例化出来的所有实例共享
的属性和方法。默认情况下,原型对象都会自动获得一个constructor(构造函数)属性,这个属性指向
prototype所在的函数。原型对象中的其他属性和方法都是从Object继承而来的。
当调用构造函数创建一个新实例后,该实例会包含一个内部属性,它是一个指针,指向构造函数的原型对象。
这里强调一点,原型对象中的属性和方法被由该构造函数的所有实例共享。对于原型对象中的基本类型的值,
所有实例在创建时,都会得到该值的一个副本拷贝。当实例修改这个基本类型值时,各个实例之间互不影响。
当原型对象中是引用类型的值时,所有实例是以指针的方式共享该引用类型的值。当某一个实例更改了该值,
其他实例指向的该值会收到影响。

当代码读取某个对象的某个属性时,先查找对象实例自身有没有该属性,如果有,就返回该属性的值。如果没找到,
就继续找对象所指向的原型对象中有没有该属性,如果有,则返回该属性的值。如果没找到,继续找父级对象实例
中有没有该属性,如果有,则返回该属性值。如果没有找到,继续找父级实例对象对应的原型对象中有没有,找到了
则返回属性的值,如果没找到,继续找父级的父级,先找父级的父级的实例对象,再找父级的父级的原型对象。
就这样一直找到Object对象为止。这也就是 原型链。原型链也正是实现继承的主要方法。

      

原型链继承的方式

        

function SuperType(){
  this.property = true;
 }
 
 SuperType.prototype.getSuperValue = function(){
  return this.property;
 }
 
 function SubType(){
  this.subproperty = false;
 }
 
 SubType.prototype = new SuperType();
 
 SubType.prototype.getSubValue = function(){
  return this.subproperty;
 }
 
 var instance = new SubType();
 console.log(instance.getSuperValue());//true
 
 
 //重写了原型对象后,SuperType所有实例对象的属性和方法 以及 SuperType的原型对象的属性和方法 都
 //作为了SubType原型对象的 属性和方法。这样 subType的所有实例就同时有了这些属性和方法

          

                

别忘了默认的Object的原型

        

所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype

          

                

确定原型与实例的关系(两种方式)

        

console.log(instance instanceof Object);//true
console.log(instance instanceof SuperType);//true
console.log(instance instanceof SubType);//true

        

console.log( Object.prototype.isPrototypeof(instance) );//true
console.log(SuperType.prototype.isPrototypeof(instance));//true
console.log(SubType.prototype.isPrototypeof(instance));//true

      

原型链的问题

        

原型共享属性的问题

        

创建子类实例时,不能想超类构造函数中传递参数

        

基于以上两点,实践中很少会单独使用原型链

    

借用构造函数

      

内容

        

在子类构造函数中调用超类构造函数。
具体是使用apply()和call()方法,可以在将来新创建的对象上执行构造函数,也可以传递参数

          

function SuperType(){
  this.colors = ['red','blue'];
 }
 
 function SubType(){
  SuperType.call(this);
 }
 
 var instance1 = new SubType();
 instance1.colors.push('black');
 console.log(instance1.colors);//['red','blue','black']
 
 var instance2 = new SubType();
 console.log(instance2.colors);//['red','blue']

          

function SuperType(name){
  this.name = name;
 }
 
 function SubType(name,age){
  SuperType.call(this,name);
  this.age = age;
 }
 
 var instance = new SubType('张三',20);
 console.log(instance.name);//张三
 console.log(instance.age);//20

      

借用构造函数的问题

        

方法都在构造函数中定义,函数复用无从谈起

    

组合继承

      

内容

        

使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样
既通过原型定义方法实现了函数复用,又能够保证每个实例都有自己的属性
instanceof和isPrototypeOf()也能识别基于组合继承创建的对象。
组合继承,称为JavaScript最常用的继承方式

          

function SuperType(name){
  this.name = name;
  this.colors = ['red','blue'];
 }
 
 SuperType.prototype.sayName = function(){
  console.log(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(){
    console.log(this.age);
   }
   
   var ins1 = new SubType('张三',10);
   ins1.colors.push('black');
   console.log(ins1.colors);//['red','blue','black']
   ins1.sayAge();     //10
   ins1.sayName();    //张三
   
   var ins2 = new SubType('李四',20);
   console.log(ins2.colors); //['red','blue'];
   ins2.sayAge();//20
   ins2.sayName();//李四

    

原型式继承

      

内容

        

这种方式与原型链相似,只是不需要创建构造函数。
当然了,也会有原型链方式的弊端,也就是属性共享的问题
此种模式,需要你必须有一个对象可以作为另一个对象的基础

          

function object(o){
  function F(){}
  F.prototype = o;
  return new F();
 }
 
 var person = {
  name:'张三',
  friends:['tom','jack']
 };
 var anotherPerson = object(person);
 anotherPerson.name = "李四";
 anotherPerson.friends.push("Bob");
 
 console.log(person.name);//张三
 console.log(anotherPerson.name);//李四
 console.log(person.friends);//['tom','jack','Bob']
 console.log(anotherPerson.friends);//['tom','jack','Bob']

    

寄生式继承

      

内容

        

这种方式与原型式继承和工厂模式类似,它同样有
着属性共享和函数无法重用的问题

          

function object(o){
  function F(){}
  F.prototype = o;
  return new F();
 }
 
 function createAnother(original){
  var clone = object(original);
  clone.sayHi = function(){
   console.log('hi');
  }
  return clone;
 }
 
 var person = {
  name:'张三',
  friends:['tom','jack']
 }
 
 var anotherPerson = createAnother(person);
 anotherPerson.sayHi();//"hi"

    

寄生组合式继承

      

内容

        

本质上是使用 寄生式继承来继承超类的原型,然后将结果指定给子类行的原型.
然后再用借用构造函数,来继承实例属性。
相比较于 组合继承调用两次超类的构造函数。它值调用了一次,并且避免了
在子类的protoType上面创建不必要的多余的属性。同时原型链还能保持不变。因此
还能正常使用instanceof和isPrototypeOf().
也完成了函数重用,各个实例的属性值互不影响
开发人员普遍认为寄生组合模式继承时引用类型最理想的继承范式

          

function object(o){
  function F(){}
  F.prototype = o;
  return new F();
 }
 
 function inheritPrototype(subType,superType){
  var prototype = object(superType.prototype);//创建对象
  prototype.constructor = subType;//增强对象
  subType.prototype = prototype;//指定对象
 }
 
 function SuperType(name){
  this.name = name;
  this.colors = ['red','blue'];
  
 }
 SuperType.prototype.sayName = function(){
  console.log(this.name);
 }
 function SubType(name,age){
  SuperType.call(this,name);
  this.age = age;
 }
 
 inheritPrototype(SubType,SuperType);
 SubType.prototype.sayAge = function(){
  console.log(this.age);
 }
 
 var person1 = new SubType('张三',10);
 person1.colors.push('black'); 
 console.log(person1.colors);//['red','blue','black']
 person1.sayName();//'张三'
 person1.sayAge();  // 10
 
 var person2 = new SubType('李四',20);
 console.log(person2.colors);//['red','blue']
 person2.sayName();//'李四'
 person2.sayAge();//20

函数

BOM

客户端检测

DOM

DOM拓展

DOM2和DOM3

事件

  

事件流

    

事件冒泡

      

由最上层到最底层html,这么一个过程

    

事件捕获

      

与事件冒泡相反 

    

DOM事件流

      

由三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段

  

事件处理程序

    

HTML事件处理程序

      

直接在页面标签中指定事件处理程序(不推荐)

        

<div id="container" onclick="alert(event.type)">
   123456
  </div>

    

DOM0事件处理程序

      

所有浏览器都支持

        

var element = document.getElementById("container");
  element.onclick = function(){
   console.log(1);
  }
  
  element.onclick = function(event){//event是事件对象
   console.log(2);
  }
  //运行结果:只输出了 2
可以通过 element.onclick = nulll;//删除事件处理程序,这样
后续的单击就不会发生任何动作

      

event.target获取当前目标对象

      

event.preventDefault()来取消事件的默认行为

      

event.stopPropagation()阻止事件冒泡

    

DOM2事件处理程序

      

浏览器兼容性:IE9+、Firefox、Safari、Chrome和Opera
addEventListener添加事件处理程序

        

var ele = document.getElementById("container");
 var handler = function(event){
  console.log(event.type);
 }
 var handler2 = function(event){
  console.log(1);
 }
 ele.addEventListener("click",handler,false);
 ele.addEventListener("click",handler2,false);
 //结果   click     1

      

使用addEventLister添加的事件处理程序必须通过removeEventListener来移除,并且
三个参数要一模一样才行

        

ele.removeEventListener("click",handler,false);
 ele.removeEventListener("click",handler2,false);

      

可以添加多个事件处理程序,比如两次click.会按照代码先后顺序执行

      

event.target获取当前目标对象

event.preventDefault()来取消事件的默认行为

event.stopPropagation()阻止事件冒泡

    

IE事件处理程序

      

attachEvent添加事件处理程序,事件处理程序会在全局作用域内运行

        

var ele = document.getElementById("container");
 var handler = function(event){
  alert(1);
 }
 var handler2 = function(event){
  alert(2);
 }
 ele.attachEvent("onclick",handler);
 ele.attachEvent("onclick",handler2);
 //结果  先弹出2,后弹出1

      

使用attachEvent添加的事件处理程序必须通过detachEvent来移除,并且
两个参数要一模一样才行

      

可以添加多个事件处理程序,比如两次click.会倒着执行

      

window.event.srcElement获取当前目标对象

window.event.returnValue = false来取消事件的默认行为

window.event.cancelBubble = true阻止事件冒泡

  

跨浏览器事件处理与事件对象

    

var EventUtil = {
  //添加监听处理器
  addHandler:function(element,type,handler){
   if(element.addEventListener){
    element.addEventListener(type,handler,false);
   }else if(element.attachEvent){
    element.attachEvent("on"+type,handler);
   }else{
    element["on"+type] = handler;
   }
  },
  //移除监听处理器
  removeHandler:function(element,type,handler){
   if(element.removeEventListener){
    element.removeEventListener(type,handler,false);
   }else if(element.detachEvent){
    element.detachEvent("on"+type,handler);
   }else{
    element["on"+type] = null;
   }
  },
  //获取事件对象
  getEvent:function(event){
  
  },
  //获取目标对象
  getTarget:function(event){
   return event.target || event.srcElement;
  },
  //取消事件默认行为
  preventDefault:function(event){
   if(event.preventDefault){
    event.preventDefault();
   }else {
    event.returnValue = false;
   }
  },
  //阻止事件冒泡
  stopPropagation:function(event){
   if(event.stopPropagation){
    event.stopPropagation();
   }else {
    event.cancelBubble = true;
   }
  }
 }

表单脚本

使用Canvas绘图

HTML5脚本编程

处理错误与调试

JavaScript与XML

E4X

JSON

Ajax与Comet

高级技巧

离线应用与客户端存储

最佳实践

性能优化

  

注意作用域

    

避免全局查找,使用全局变量和函数肯定要比局部的开销更大,因为它涉及到作用域链的查找

  

选择正确的方法

    

优化循环。使用减值迭代,从最大值开始,在循环中不断减值的迭代器更加高效。
简化终止条件,由于每次循环过程都会就散终止条件,所以必须保证它尽可能快

    

当循环次数是固定的,消除循环,使用多次函数调用,速度往往更快

    

当循环次数不固定时,数据量大的时候,使用Duff装置技术

  

优化DOM交互

    

最小化现场更新

  

代码压缩

    

http压缩(比如开启nginx的http压缩)

自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论