Javascript知识点汇总

ES5知识点按层级汇总回顾,通过这个层级关系把知识点关联起来,实际使用时通过项目来融合解决问题。

第一层:ES5、ES6基础语法

1.1 语法基础
1.2 变量、作用域、基本类型。let 和 const 声明。基本类型的一些拓展值属于类型。set map 数据结构以及操作方式。
1.3 引用类型。引用类型的一些拓展,解构赋值。深复制与浅复制。
1.4 面向对象编程。原型链,组合继承。class 继承
1.5 函数表达式
1.6 BOM对象 history global window document
1.7 DOM对象
1.8 事件。事件捕获、事件冒泡、事件对象、事件代理...
1.9 表单及表单事件
1.10 HTML5新特性
1.11 JSON与Ajax。 json转化函数,ajax两种方式请求的过程,promise 的用法。跨域的几种方式。
1.12 离线存储
1.13 高级技巧
1.14 模块 AMD CommonJS

第二层:通过ES语法所能实现的几个大的功能模块,含项目构建、交互、表单验证、数据传输、数据存储五个大块

2.1项目构建
涉及到的知识点:nodejs npm webpack ES6 的 import、export 各类型的 loader
webpack的构建流程和注意知识点。

2.2交互
涉及到的知识点:BOM DOM PC事件 移动端事件

2.3表单验证
涉及到的知识点:DOM中的 form form 事件 正则表达式
验证、提交、阻止提交、捕获select

2.4数据传输
涉及到的知识点:Ajax json JsonP http

2.5数据存储
涉及到的知识点:cookie localStorage session

第三层:各个模块下实际功能案例

各种轮子的再造
3.1 布局
3.2 导航
按钮
form
下拉框

...

1.1语法基础

六种基本数据类型 Undefined Null Boolean String Number Symbol,一种复杂数据类型Object

typeof 是一个操作符,不是方法,后面可以跟变量,也可以跟数值字面量。

typeof null 返回 object ,因为null被认为是一个空对象的引用(指针)。

console.loc(typeof null);       //object

声明了变量,但是未赋值,则调用 typeof 返回的是undefined,因为Udefined类型只有一个值,就是undefined,最好是声明了变量就赋值,避免与不存在的变量返回相同的undefined造成混淆。

Null类型也只有一个值,null。当你声明一个变量将来用于保存对象时,最好将这个对象赋值为null,将来只要检测变量是否等于null,就可以知道变量是否已经赋值。例子

if(car != null){
    //对car变量执行一些操作
}

Boolean 类型只有两个值,true 和 false。ES 中所有类型的值都有与这两个boolean 值等价的值。可以调用 Bollean() 方法将一个值转化为布尔值。

Boolean("任何非空字符串");         //true
Boolean("任何非零数字值,包括无穷大");         //true
Boolean("任何对象");         //true
Boolean("空字符串");         //false
Boolean("0和NaN");         //false
Boolean("null");         //false
Boolean("undefined");         //false

Number 类型,整形和浮点型,类型所带的方法,均派生自包装类型。

特殊的表示方法 5e3 代表 5x10^3 即 5000。
最大值 1.7976931348623157e+308,用 Number.MAX_VALUE 表示,超出这个值,则会表示为 Infinity。
最小值 5e-324,用 Number.MIN_VALUE 表示,小于这个值,则会表示为 -Infinity,可以用 isFinity() 来检测是否是在正负最大值之间,在的话返回 true 。例子

var numberA = 848448;
isFinite(numberA);          // true

NaN表示一个本来要返回数值的操作数未返回数值的情况,可以用isNaN检测是否是NaN,参数接收任何类型。例子

isNaN(NaN);             //true
isNaN(10);             //false 本来要返回数值,却仍然返回数值
isNaN("10");             //false 字符串10能被转化为数值
isNaN("blue");             //true blue不能被转化为数值
isNaN(true);             //false true能被转化为数值1

Number()数值转换方法,因为会出现不可控现象,不常用。例子

Number(true);       //1
Number(false);       //0
Number(10);       //10
Number(null);       //0
Number(undefined);       //NaN
Number("011");       //11
Number("01.1");       //1.1
Number("0xf");       //相同大小的十进制数值
Number("");       //0
Number("除上述格式之外的其他字符");       //NaN
Number(object);       //先调用对象的valueOf(),依照前面的规则转换,如果返回NaN。则调用对象的toString()方法,再次依照前面的规则转换

parseInt(123456,16);    //取整方法可以指定转换的进制,按照16进制来转换
ParseInt("");           //返回NaN 与Number()方法有所不同

parseFloat();           //不能设定转换进制,统一十进制
parseFloat("00.2145");        //0.2145,会忽略前导
  • 字符串类型
    ES5中字符串是不可变的,一旦创建,他们的值就不能改变。要改变某个变量保存的字符串,首先销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量。

每个字符串值都可以调用toString()方法来转化为字符串,null和undefined没有。例子

var aaa = "hahaha";
aaa.toString();

字符串中包含一些特殊的转义序列,表示非打印字符。

\n  换行
\t  制表
\b  退格
\'
\"
\r  回车
\f  进纸
\\  斜杠
\unnnn  十六进制表示一个Unicode字符

在不知道一个变量是否包含 null 或者 undefined 的情况下,可以调用 String() 方法来转换,该函数能够将任何类型的值转换为字符串,包括 null 和 undefined。其原理如下,在能够调用 toString() 的情况下,调用 toString();不能调用的情况下,null 返回字符串 “null”,undefined 返回字符串 “undefined”。

Object类型,可以通过new字符加上要创建的对象类型的名称来创建。 每个对象都是Object类型的实例,都拥有Object类型共用的属性和方法

  • constructor: 保存着用于创建对象的函数,构造函数,也就是Object()

  • hasOwnProperty(propertyName): 用于检查属性是否位于实例中,是返回true,位于原型返回false。propertyName必须是

isPrototypeOf(object): 用于检查传入的对象是否是当前对象的原型

propertyIsEnumerable(propertyName): 用于检查给定属性是否能够使用for-in语句来枚举。例子

var myObject = new Object();

myObject.name = "wenzehai";
myObject.subject = {
     class: 105,
     subjectName: "yuwen"
};

alert(myObject.hasOwnProperty("subject"));                    //true
alert(myObject.propertyIsEnumerable("subject"));            //true

toLocalString(): 返回对象的字符串表示,该字符串与执行环境的地区对应。

toString(): 返回对象的字符串表示。

valueOf(): 返回对象的字符串、数值或布尔值表示。即返回值是带有类型的。例子

var myObject = new Object();

myObject.name = "wenzehai";
myObject.subject = {
    class: 105,
    subjectName: "yuwen"
};

var myFunction = function(){
    return -1;
};

var myArr = ["aaa","bbb"];

console.log(myObject.valueOf());            //object类型 一个完整的对象
console.log(myObject.toString());           //[object object]
console.log(String(myObject));              //[object object]

console.log(myFunction.valueOf());          //function类型 一个函数
console.log(myFunction.toString());        //function表达式的字符串表示      function(){return -1;}
console.log(String(myFunction));            //function表达式的字符串表示     function(){return -1;}

console.log(myArr.valueOf());          //Array类型 ["aaa", "bbb"]
console.log(myArr.toString());        //aaa,bbb
console.log(String(myArr));            //aaa,bbb

  • 操作符
    递增操作符和递减操作符分为前置和后置,后置操作符是在包含他们的语句被求值之后才执行。

一元加减操作符

  • 和 - 操作符代表数值的加减,会自动调用 Number() 方法转换。当 + 操作非数值时,会有特殊处理,先调用 Number(),遇到字符串,会调用 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
s2 = +s2;           //1.1
s3 = +s3;           //NaN
b = +b;           //0
f = +f;           //1.1
o = +o;           //-1

布尔操作符,包含三个值 ! && ||
! 可以应用于ES中的任何值,无论这个值是什么类型。他先将值用Boolean()转化为布尔值,然后取反。

逻辑与可以应用于任何类型的操作数,而不仅仅是布尔值。当有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值。

  • 如果第一个操作数是对象,则返回第二个操作数

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

  • 如果两个操作数都是对象,则返回第二个对象。

  • 如果第一个操作数是null,则返回null

  • 如果第一个操作数是NaN,则返回NaN

  • 如果第一个操作数是undefined,则返回undefined

逻辑与属于短路操作,第一个为false,则不会执行第二个操作数的判断,例子

var found = true;
var result = (found && someUndefinedVariable);          //报错,逻辑与中不能出现未声明的变量

逻辑或
逻辑或可以应用于任何类型的操作数,而不仅仅是布尔值。当有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值。

  • 如果第一个操作数是对象,则返回第一个操作数
  • 如果第一个操作数的求值结果为false,则返回第二个操作数
  • 如果两个操作数都是对象,则返回第一个操作数
  • 如果两个操作数都是null,则返回null
  • 如果两个操作数都是NaN,则返回NaN
  • 如果两个操作数都是undefined,则返回undefined
var myObject = firstObject || backupObject;         //当firstObject为空时,返回后备的对象

乘除取模操作符
当以上操作符操作非数值时,会自动调用Number()转型函数,转完之后再求值,需要注意Infinity与符号的运算结果,基本上很少用到。

加操作符
一般做数值处理,如果操作数值中有一个是字符串,则会自动把另一个数值转化为字符串,然后拼接起来。如果是对象、数值布尔值,则会调用 toString(),如果是 null 或者 undefined,会调用 String()。

关系操作符
关系操作符都返回布尔值

  • 如果两个数都是数值,则进行数值比较
  • 如果两个数值都是字符串,则比较两个字符串对应的字符编码值
  • 如果其中一个是数值,则将另一个转化为数值
  • 如果其中一个是对象,则调用这个对象的valueOf()方法,用得到的结果进行前面规则执行比较,如果没有valueOf()方法,则执行toString()
  • 如果数值是布尔值,则将其转化为数值,然后再比较
  • 任何值与NaN比较,结果都返回false

相等操作符
相等和不相等 == !== 先转换为数值类型,然后再比较.

  • 如果有一个操作数是布尔值,则在比较相等性之前,先把布尔值转化为数值,false转化为0,true转化为1
  • 如果有一个操作数是字符串,另一个是数值,则先将字符串转化为数值再比较
  • 如果有一个操作数是对象,则先调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较。
  • null和undefined是相等的
  • 如果其中一个是NaN,则相等返回false,不相等返回true;两个数值都是NaN,相等返回false
  • 如果两个操作数都是对象,则比较是不是同一个对象,如果都指向同一个对象则,则相等

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

条件操作符 ? :

逗号操作符可以赋值,总是返回表示中的最后一项。例子

var num1 = (1,2,3,4,5);         //num1的值为5

// 常用语句

var i=0;
while (i<10){
    alert(1);
    i++;
}


var j = 0;
do {
    alert(2);
    j++;
} while(j < 10);

for(var propertyName in myObject){
    console.log(propertyName);          //输出属性名
    console.log(myObject[propertyName]);        //输出属性值
}

//label语句的表达式   label: statement  例子
start: for(var i=0; i< count; i++){
    alert(i);
}

for(var i=0; i<10; i++){
    if(i%5 == 0){
        break;
    }
    console.log(3);
}

with(location){
    var qs = search.substring(1);
    var hostName = hostname;
    var url = href;
}

//switch语句的 index  和 case 值可以是字符串、数值、甚至表达式
switch (index) {
    case 1:
        console.log("1111");
        break;
    case 2:
        console.log("2222");
        break;
    default :
        console.log("other");
}

函数类型
函数如果没有 return,则执行完毕后默认返回 undefined;return 可以提前结束函数执行。

函数的参数为一个类数组,可以通过 arguments 对象来访问这个参数类数组;命名参数只提供便利,但不是必须的;他的值永远与命名参数的值保持一致;如果不传参,则返回 undefined

function  doAdd(num1,num2){
    arguments[1] = 10;
    console.log(arguments[0] + num2);
}

doAdd(1,2);         //11
doAdd(1);           //NaN

1.2 变量、作用域、基本类型。值属于类型

变量中可以包含两种类型值,基础类型值和引用类型值,其中值的类型从属于 ES 中的数据类型。

其中基本类型值的类型有:Undefined Null Boolean Number String, 这物种类型直接操作数据;

引用类型值: Object,操作引用类型操作的是引用,而不是值。二者的区别在于引用类型可以自己为引用类型自定义属性和方法。

复制变量值,用=号赋值。基本类型的复制,是值的复制,拷贝一份,两个值不干扰。

引用类型的复制,是拷贝的引用地址,改变一个对象的属性或方法,会影响另一个对象

ES 中的参数的传递均按照值来传递,向函数传参时,其实就是把一个变量的值复制给arguments 对象的一个元素(一个局部变量)。当把基本值复制给 arguments 对象时,函数在执行完毕后,arguments 对象就消失了;同样,当把一个引用类型复制给arguments 对象时,函数执行完毕,里面的形参arguments对象也消失了,但是他对传递进来的引用类型所做过的操作还是保留了。说白了,就是当你传递一个引用类型给函数时,里面有两个变量,都指向同一个内存位置,都做一样的修改,但是函数执行完毕后,里面的变量会消失,外面的变量还在。

判断变量的类型的操作符 instanceof 。如果变量是给定引用类型的实例,那么 instanceof 操作符就会返回 true 。例子

alert(person instanceof String);
alert(person instanceof Array);
alert(person instanceof Object);
  • 执行环境
    ES 中定义变量或函数有权访问其他数据,决定来它们各自的行为,每个环境有一个变量对象,类似于函数的 arguments 变量对象,环境中定义的所有变量和函数都保存在这个对象中。

在web浏览器中,全局环境被认为是 window 对象,所有全局变量和函数都是作为 window 对象的属性和方法创建。

每个函数也有自己的执行环境。当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。

延长作用域链的方法 try-catch 语句的 catch 块和 with 语句。 例子

function buildUrl() {
    var qs = "?debug=true";
    with(location){
        var url = href + qs;            //这里的href其实是location变量对象中的变量
    }
    return url;                         //url作为函数内部的一个变量,可以被返回,个人理解的是location变量对象包含函数变量对象
}

没有块级作用域。

声明的变量会自动添加到最接近的环境中。在函数内部,最接近的环境就是函数的局部环境;在with语句中,最接近的环境是函数环境。

垃圾收集机制,为了释放内存,进行垃圾回收,常用的标记清除和引用计数,其中标记清除比较普及。

1.3 引用类型

引用类型的值(对象)是引用类型的一个实例。引用类型是一种数据结构,用于将数据与功能组合在一起。

  • Object类型
var obj = new Object();
var obj1 = {};

Array类型,实例的方法继承自 Array 类型的原型对象,具有相当多的属性和方法

属性
length 不仅可以读,还可以写。

方法
Array.isArray(arr) 检测是不是数组
toString()
toLocalString()
valueOf() 继承自Object类型,返回的还是数组,其实调用了每一项的 toString 方法
join() 将数组转化为字符串,例如 arr.join(",")

push() 末尾推入,可以推入单个项,也可以推入数组,返回推入后数组的长度
pop() 末尾移除,移除一项,返回移除的项
shift() 移除数组第一项,并且返回移除项
unshifit() 第一位推入数组项,返回新数组的长度

sort() 排序,可以传入比较函数,即正负值。注意sort()方法比较的时字符串的大小,即调用每一项的 toString() 再比较,返回排序后的数组
reverse() 反转数组,返回原来的数组

concat() 将两个数组组合
slice() 基于当前数组中的一项或多项创建一个新数组
splice() 删除、插入、替换一个数组

indexOf() 查找位置
lastIndexOf()

every() 数组的每项运用函数处理,其中每项的结果返回true,则返回true,如果其中一项返回false,则返回false
some() 与every()方法取反
forEach() 循环,不返回值
map() 循环,返回处理后的值,组成一个新数组
filter() 循环处理,返回符合条件的值

reduce() 归并方法
reduceRigth() 右向归并方法

//splice方法的用法,返回移除项,splice(位置,项数,插入的数组)
var colors = ["red","green","blue"];
var removed = colors.splice(0,1); //删除第一项
console.log(colors); //["green","blue"]
console.log(removed); //["red"]

removed = colors.splice(1,0,"yellow","orange"); //从位置1开始插入两项,意味着位置1被新项替换掉了
console.log(colors); //["green","yellow","orange","blue"]
console.log(removed); //返回一个空数组

removed = colors.splice(1,1,"red","purple"); //插入两项,删除位置1一项
console.log(colors); //["green","red","purple","orange","blue"]
console.log(removed); //["yellow"]

//迭代方法
var num = [1,2,3,4,5];
var result = num.forEach(function (value) {
    console.log(value);
    return value*2;
});
console.log(result);
  • Date类型
    通过new Date()创建的函数,返回 Sun Sep 09 2018 09:33:38 GMT+0800 (中国标准时间) 类似于这种格式的时间。
var date = new Date();
console.log(date); //Sun Sep 09 2018 09:33:38 GMT+0800 (中国标准时间)

通常可以为new Date()传入毫秒数来生成对应的时间,但是毫秒数不好操作,所以默认调用Date.parse()处理一些类似字符串的日期输入,传入的格式通常可以是

//“月/日/年”,例如6/13/2004 
console.log(new Date("4/13/2004"))

//英文月名,日,年,例如 January 12, 2006
console.log(new Date("January 23, 2006"));

//英文星期几 英文月名 日 年 是:分:秒 时区 ,例如 Tue May 25 2004 00:00:00 GMT-0700
console.log(new Date("Thu May 24 2018 15:24:30 GMT+0800")) //这里还是有点问题,星期几是根据日期算出来的,所以不填也没有关系

//ISO 拓展格式 YYYY-MM--DDTHH:mm:ss.sssZ ,例如2004-05-25T00:00:00
console.log(new Date("2018-05-24T13:12:21.456")) //毫秒是三位数

//最简单的方法
var date = new Date("September 9, 2018"); //就创建来一个时间对象,此时时分秒毫秒都是0

var date1 = new Date("September 9, 2018");
console.log(date1.getTime()) //1536422400000
console.log(date1.getDate()) //9
console.log(date1.getMonth()) //8 少一位数
console.log(date1.getMinutes()) //0
console.log(date1.getMilliseconds()) //0
console.log(date1.getSeconds()) //0
console.log(date1.getDay()) //0 星期天

//获取当前时间
Date.now(); //IE9+

//原始的获取时间戳方法
var start = +new Date();
var end = +new Date()

//通过getTime()方法可以获得相应的毫秒数
console.log(date.getTime()); 

正则表达式

var expression = / pattern / flags

pattern 中的内容如果有正则特殊字符,需要用\转义,包括( { [ \ ^ $ | ) ? * + . ] }

//flag i g m,其中m代表多行

正则用法,一是找到匹配项,然后返回true或者false;二是找到匹配项,返回对应的匹配项;三是返回多个匹配项;

具体实践有配合字符串方法search()和replace(),自身正则的方法有test()和exec()

字面量模式范例:
var pattern = /at/g //全局匹配at两个字母的组合

var pattern1 = /[bc]at/i; //不区分大小写匹配第一个bat或者cat

var pattern2 = /.at/gi; //匹配所有一at结尾的3个字符,不区分大小写

例子:
search() 方法 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,并返回子串的起始位置。

var str = "Visit Runoob!";
var n = str.search(/Run/i); //6
console.log(n);

var newstr = str.replace(/Run/i, "Andy"); //返回新的字符串Visit Andyoob!
console.log(newstr);

表达式模式
方括号用于查找某个范围内的字符:

[abc] //查找方括号之间的任何字符

[0-9] //查找0-9

(x|y) //查找任何以|分隔的选项

// 元字符是拥有特殊含义的字符

\d //查找数字

\s //查找空白字符

\b //匹配单词边界

\uxxxx //查找以十六进制数 xxxx 规定的 Unicode 字符。

// 量词

n+ //匹配任何至少包含一个n的字符串

n* //匹配任何包含0个或多个n的字符串

n? //匹配任何包含零个或一个n的字符串

test() 方法是一个正则表达式方法,用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false。

var patt = /e/;
patt.test("The best things in life are free!");
/e/.test("The best things in life are free!")    //一行的写法

exec() 方法是一个正则表达式方法。exec() 方法用于检索字符串中的正则表达式的匹配。该函数返回包含地一个匹配项的数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。即使匹配项包含g,也只会返回一项。但是添加了g之后,对字符串调用exec()会多次匹配,index值会改变。

var patt1=new RegExp("e+","ig");
document.write(patt1.exec("The beset things in life aere efree")); //e

JS 判断输入字符串是否为数字、字母、下划线组成;JS 判断输入字符串是否全部为字母;JS 判断输入字符串是否全部为数字;ruoob均有范例

Function类型

每个函数都是Function类型的实例
每个function都有两个属性,arguments和this,他们是两个对象,arguments对象保存着函数的参数,这个对象还有一个callee的属性,保存着一个指针,指向拥有这个arguments对象的函数。用途在阶乘函数上,例子

//阶乘函数,递归算法
function factorial(num) {
  if(num <= 1) {
    return 1;
  } else {
    return num * arguments.callee(num-1);
  }
}

this引用的是函数执行环境的环境对象——或者也可以说是this值。函数的名字仅仅是一个包含指针的变量而已。

ES5也规范了另一个函数对象的属性:caller。这个属性中保存着调用当前函数的函数引用(指针,即哪个函数调用了当前函数)。

ES5中还定义了一个方法bind()。这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。具体参见范例

function inner(){
  alert(inner.caller);
}

function call(){
  inner();
}

call()   //alert(call());

//为了实现松散耦合,更改函数名也不会影响功能,可以改为如下形式。
function inner() {
  alert(arguments.callee.caller);
}
//... 后面的call同上

函数的其他属性
length 表示函数希望接收的命名参数的个数

prototype 函数的原型对象
对于ES中的引用类型而言,prototype是保存他们所有实例方法的真正所在。prototype属性不可枚举。

每个函数都有两个非继承而来的方法 call() 和 apply(),用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值,扩展了函数赖以运行的作用域。二者的区别在于接收参数的不同。具体可参见范例

function sum(num1, num2) {
 console.log(this.valueOf());
 return num1+ num2;
}

//在函数中调用
function callSum1(num1, num2) {
 console.log("callSum1的this:" + this);  //[object Window]
 return sum.apply(this,arguments); //在callSum1函数中(另一个作用域)调用sum函数,this值为调用函数的作用域,直接调用为window,this值也被传入了sum函数中。arguments可以替换为[num1,num2]这样的数组
}
console.log(callSum1(10,10));  //callSum1的this结果为:[object Window]

//在对象中调用
var o = {
 a: 12,
 b: function () {
   console.log(this);
   return sum.apply(this,arguments);
 }
};

console.log(o.b(10,10));    //{a: 12, b: ƒ} 调用b时打印出b的this值,{a: 12, b: ƒ} 这里打印的为sum函数的this值,是继承了o对象, 20


call() 方法与 apply 方法一样,第一个数传入this值,第二个参数必须逐一列举出所有传进去的参数,也可以不传参数。例子

function sumA(num1, num2) {
  console.log(this.valueOf());
  return num1+ num2;
}
//在函数中调用
function callSumB(num1, num2) {
  console.log("callSuB的this:" + this);
  return sumA.call(this,num1,num2);
}
console.log(callSumB(10,10));

结果为:20
callSuB的this:[object Window]

当传入的第二个参数为arguments时,结果会返回undefined,证明只能单独一个个传参数进去结果为:undefined

bind()方法范例 ES5中还定义了一个方法bind()。这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。具体参见范例

window.color = "pink";
var o = {color: "blue"};
function sayColor1() {
  console.log(this);   //直接调用为[object Window]
  console.log(this.color);
}
var objectSayColor = sayColor1.bind(o);
objectSayColor(); //blue,,即使是在全局作用域中调用,也不受影响。
  • 基本包装类型

基本包装类型包含String类型、Boolean类型、Number类型,主要在其原型对象上定义了诸多类型独有的方法和属性;此包装类型只存在于代码执行的瞬间。

每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,操作完之后销毁对象。过程相当于

var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;

Number包装类型独有的方法

toFixed() //按照指定的小数位返回数值的字符串表示,注意,是数值转化为字符串 var num = 10.005 num.toFixed(2) //10.01 会自动舍入

toExponential(1); //转化为指数表现形式;

toPrecision() //返回最合适的格式

String包装类型

charAt() //返回某个位置的字符

charCodeAt() //返回某个位置的字符的字符编码

concat()方法 //拼接字符串,也可以用+拼接

通过字符串创建新字符串

  • slice(start,end) //返回从开始至结束位置的字符串,当传入的参数为负值时,会讲传如的参数与字符串的长度相加
  • substr(start,length) //返回从开始位置至指定长度的字符串,当传入的参数为负值时,讲第一个参数的负值与字符串长度相加,相当于逆序算,第二个参数转化为0
  • substring(start,end) //返回从开始至结束位置的字符串,当传入的参数为负值时,会讲所偶的负值都转化为0
  • indexOf()
  • lastIndexOf()
  • trim() //ES5新增方法trim(),创建一个字符串的副本
  • toLowerCase()
  • toLocaleLowerCase()
  • toUpperCase()
  • toLocaleUpperCase()
  • match() //字符串匹配,只接收一个正则表达式对象做为参数,可以时字面量形式,也可以new出来的正则。匹配到返回一个数组,数组中的第一项时与整个模式匹配的字符串,之后的每一项保存着与着与
//正则表达式中的捕获族匹配的字符串
var text = "cat, bat, sat, fat";
var pattern = /.at/;

var matches = text.match(pattern);
alert(matches.index) //0
alert(matches[0]) //"cat"
alert(pattern.lastIndex); //0
  • search() //传入字符串或者正则表达式,返回匹配到的项的索引值,如果没有找到匹配项,则返回-1
var text = "cat, bat, sat, fat";
var pos = text.search(/at/);
alert(pos); //1
  • replace() //接收两个参数,第一个可以时正则对象或则字符串(这个字符串不会被转化为正则对象),第二个参数可以时一个字符串或者一个函数。如果第一个擦哪壶时字符串,那么只会替换第一个子字符串 。要想替换所有字符串,唯一的办法就是提供一个正则表达式,而且要指定全局g。参见范例
var text = "cat, bat, sat, fat";
var result = text.replace("at","ond");
console.log(result); //"cond,bat,sat,fat"

result1 = text.replace(/at/g,"ond");
console.log(result1); //"cond,bond,sond,font"

//第二个参数为函数模式,具体可以参见高三128页;
function htmlEscape(text){
  return text.replace(/[<>"&]/g,function (match, pos, originalText) {
    switch (match) {
      case "<":
        return "<";
      case ">":
        return ">";
      case "&":
        return "&";
      case "\"":
        return """
    }
  })
}

console.log(htmlEscape("

hello world

"));
  • split() //使用特殊符号分割字符串,返回数组,也可以传入正则表达式;有浏览器兼容性问题
  • localeCompare() //比较两个字符串,字符串排列位置的比较,参见高三129页

单体内置对象,由ES实现提供,不依赖于宿主环境,这些对象在ES程序执行之前就已经存在了,不需要显式d的实例化内置对象,他们已经实例化了。常见的内置对象有Object,Array,String,Global,Math

Gloabl对象

常见的方法和属性
encodeURI() 方法可以对通用资源标志符进行编码,有效的URI中不能包含某些字符,例如空格,通过这两个方法,自动将一些无效的字符用UTF-8编码替换,从而让浏览器能偶接受和理解。

  • encodeURI()主要用于对整个URI进行编码,不会对本身属于RUI的特殊字符进行编码,例如冒号、正斜杠、问好、#号;

  • encodeURIComponent() //会对所有非标准字符进行编码,一般使用此方法

  • decodeURI()

  • decodeURIComponent()

  • eval() //执行任何代码

  • global身上的属性有
    undefined、NaN、Infinity、Object Array Function Boolean String Number Date RegExp Error EvalError RangeError ReferenceError SyntaxError TyperError URIError

Window对象为浏览器的默认全局对象

Math对象,包含一些常见的数学处理方法
ceil floor round random

1.4面向对象编程

  • 面向对象
    面向对象相当于制造对象的模子,模子有些什么特征,就相当于作出来的产品有些什么样的属性;模子的造型决定了造出来的产品有什么功能,相当于产品身上有什么方法。面向对象编程要解决的问题是创建对象,具有相同的属性和方法,但是又不互相影响。

工厂模式

    function createPerson(name,age,job) {
        var o = new Object();
        o.name = name;
        o.age = age;
        o.job = job;
        o.sayName = function () {
            alert(this.name);
        };
        return o;
    }
    var person1 = createPerson("wenzehai",29,"package Engineer");
    var person2 = createPerson("zhangsan",25,"worker");
    /*alert(typeof person2);*/

工厂模式虽然解决了创建对象问题,却没有解决对象识别的问题(即怎样知道一个对象的类型)

构造函数模式,这里指的是自定义构造函数

    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.sayName = function () {
            alert(this.name);
        }
    }
    var person3 = new Person("lisi",33,"doctor");
    var person4 = new Person("wangwu",33,"driver");

   /* alert(person3 instanceof Object); */          //既是Object的实例
   /* alert(person3 instanceof Person);  */         //也是Person的实例

    //可以将构造函数当做一个普通函数来使用,以下是在另一对象中调用构造函数
    var o = new Object();
    Person.call(o,"zhaoliu",22,"chief");
//    o.sayName();        //zhaoliu

构造函数的问题在于方法重复声明,两个方法不相等,不指向同一个内存位置,如果把方法放在构造函数外面,则没有封装性可言,而且只能被构造函数所调用,用处不大。

  • 原型模式
    我们创建的每个函数都一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。等价于prototype就是通过调用
    构造函数而创建的那个对象实例的原型对象。可以让所有实例共享它所包含的属性和方法是原型模式的好处。
    function Person1() {}
    Person1.prototype.name = "Nicholas";
    Person1.prototype.age = 29;
    Person1.prototype.job = "teacher";
    Person1.prototype.sayName = function () {
        alert(this.name);
    };
    var person5 = new Person1();
//    person5.sayName();          //Nicholas

    var person6 = new Person1();
//    person6.sayName();          //Nicholas

//    alert(person5.sayName == person6.sayName);      //true

无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。当用构造函数创建了一个新实例后,该实例的内部将包含一个指针(内部属性,无法被js代码正常访问),指向构造函数的原型对象。ECMA-262管这个指针叫[[prototype]],现代浏览器Firefox Safari Chrome 在每个对象上都支持一个属性proto

当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性。但是原型对象中的那个属性还是存在,只是无法访问,不过,通过delete操作符则可以完全删除实例属性,从而能够重新访问原型中的属性。

有两种方式使用in操作符:单独使用和再for-in循环中使用。在单独使用时,in操作符会在通过对象 能够访问给定属性时返回true,无论该属性存在于实例中还是原型中

    //alert("name" in person5);       //true

在使用for-in循环时,返回的是所有能够通过对象访问的,可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。

  • 更简单的原型语法
    function Person2() {
    }
    Person.prototype = {
        name: "wenqinglin",
        age: 56,
        job: "farmer",
        sayName: function () {
            alert(this.name);
        }
    };

上面的代码将等于一个以对象字面量形式创建的一个新对象。最终结果相同,但是constructor属性不在指向Person2了。以上字面的写法,本质上完全重写了prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数,因为字面量形式创建对象,对象的原型中的constructor属性指向Object构造函数),不再指向Person2函数。

为了避免产生constructor混乱,所以最好用下述写法。

    function Person3() {

    }
    Person.prototype = {
        constructor: Person3,
        name: "wenqinglin",
        age: 56,
        job: "farmer",
        sayName: function () {
            alert(this.name);
        }
    };

由于在原型中查找值的过程是一次搜索,因此我们队原型对象所做的任何修改都能够立即从实例上反映出来——即使是县创建了实例后修改原型也照样如此。原因归结为实例与原型之间的松散连接关系。

    function Person4() {
    }
    var peoplewen = new Person4();

    Person.prototype = {
        constructor: Person4,
        name: "wenqinglin",
        age: 56,
        job: "farmer",
        sayName: function () {
            alert(this.name);
        }
    };

    //peoplewen.sayName();        //会报错,peoplewen.sayName is not a function,因为创建实例在定义原型对象的前面,相当于把原型对象重新写了一遍,指针找到的是原先的原型,找不到方法。

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

原型模式存在的主要问题在于属性共享,在属性是引用类型时,会互相共享,修改一个实例的引用类型值属性时,另一个实例的属性会受到影响。

组合使用构造函数模式和原型模式,构造函数用于定义实例属性,而原型模式用于定义方法和共享的属性。每个实例都会有自己的一份实例属性的副本,同时共享着对方法的引用还支持向构造函数传递参数,这是一种应用最广泛,用来定义引用类型的一种默认模式

    function Person5(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = ["Andy","Tom"];
    }

    Person5.prototype = {
        constructor: Person5,
        sayName: function () {
            alert(this.name);
        }
    };

    var person7 = new Person5("haoyingxue",29,"businessman");
    var person8 = new Person5("wenzehai",29,"teacher");

    person7.friends.push("Bill");
    console.log(person7.friends);           //["Andy", "Tom", "Bill"]
    console.log(person8.friends);           //["Andy", "Tom"]

  • 继承
    利用原型让一个引用类型继承另一个引用类型的属性和方法。简单回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,加入我们让原型对象等于另一个类型的实例,结果会怎么样呢?显然,此时原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。
    实现原型链的基本模式如下:

    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();
//    alert(instance.getSuperValue());                //true

子类型有时候需要覆盖超类型中的某个方法,或者需要添加超类型中不存在的某个方法。但不管怎样,给原型添加方法的代码一定要放在替换原型的语句之后。即,重新添加的语句需要放在SubType.prototype = new SuperType() 后面,这样新实例调用的是新方法,老实例调用的是老方法,两个方法不一样。另外,在 subType.prototype = new SuperType() 语句执行后,不能用字面量的形式来创建原型方法,因为相当于又重写了原型对象。

原型链的问题1,原来存在于实例中的引用类型值属性变成了原型属性,会相互引用,造成混乱。
问题2,在创建子类型的实例时,不能向超类型的构造函数中传递参数。实践中很少单独使用原型链。

借用构造函数,即在子类型构造函数的内部调用超类型构造函数。

function SuperType() {
    this.colors = ["red","blue"];
}
function SubType() {
    SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("green");
//alert(instance1.colors);            //red,blue,green

    var instance2 = new SubType();
    instance2.colors.push("yellow");
//    alert(instance2.colors);        //red,blue,yellow,虽然相互引用,而且属性是在构造函数内部,并没有混乱。

//有一个好处,可以在子类型构造函数中向超类型构造函数传递参数。例:
    function SuperType4(name) {
        this.name = name;
    }
    function SubType() {
        SuperType4.call(this,"zhangsanfeng");           //可以传递参数
        this.age = 29;
    }
    var instance5 = new SubType();
//    alert(instance5.name);
//    alert(instance5.age);

组合继承方式(首选方式)

    function SuperType6(name) {
        this.name = name;
        this.colors = ["red","blue","green"];
    }
    SuperType.prototype.sayName = function () {
        alert(this.name);
    };
    function SubType6(name, age) {
        SuperType6.call(this,name);
        this.age = age;
    }
    SubType6.prototype = new SuperType();
    SubType6.prototype.constructor = SubType6;                  //需要注意顺序,同时,不能用字面量的形式,因为相当于重写了
    SubType6.prototype.sayAge = function () {
        alert(this.age);
    };
    var instance6 = new SubType6("Nicholas",29);
    instance6.colors.push("pink");
//    alert(instance6.colors);
//    instance6.sayName();
//    instance6.sayAge();

    var instance7 = new SubType6("Andy",35);
//    alert(instance7.colors);
//    instance7.sayName();
//    instance7.sayAge();

寄生组合式继承,理想中继承模式

function inheritPrototype(subType, superType) {         //用一个对象衔接了一下
    var prototype = Object(superType.prototype);        //创建对象,这里的Object()是window身上自带的创建对象的构造函数
    prototype.constructor = subType;                    //增强对象
    subType.prototype = prototype;                      //指定对象
}

//注意生成方式,Object()方法传入一个对象,生成一个对象

var o = Object({a:10});

    console.log(o);         //{a: 10}                       一个对象形式的对象

    var o1 = Object("abc");

    console.log(o1);         //String {"abc"}               一个string对象

function SuperType7(name) {
    this.name = name;
    this.colors = ["red","blue","green"];
}
SuperType7.prototype.sayName = function () {
    alert(this.name);
};
function SubType7(name, age) {
    SuperType7.call(this,name);
    this.age = age;
}
inheritPrototype(SubType7, SuperType7);

SubType7.prototype.sayAge = function () {
    alert(this.age);
};
var instance7 = new SubType7("Nicholas",29);
    instance7.colors.push("pink");
//    alert(instance7.colors);
//    instance7.sayName();
//    instance7.sayAge();

工厂模式,使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。这个模式后来被构造函数取代。

构造函数模式,可以创建自定义引用类型,可以像创建对象实例一样使用new操作符。不过,构造函数模式也有缺点,即它的每个成员都无法得到复用,包括函数。由于函数可以不局限于任何对象(即与对象具有松散耦合的特点),因此没有理由不在多个对象间共享函数。

原型模式,使用构造函数的prototype属性来指定哪些应该共享的属性和方法。组合使用构造函数和原型模式,使用构造函数定义实例属性,而使用原型定义共享的属性和方法。

JavaScript主要通过原型链实现继承。原型链的构建是通过讲一个类型的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问超类型的所有属性和方法,这一点与基于类的继承很相似。原型链的问题是
对象实例共享所有继承的属性和方法,因此不适宜单独使用。解决这个问题的奇数是借用构造函数,即在子类型构造函数你数的内部调用超类型构造函数。这样就可以做到每个实例都具有自己的属性,同事还能保证
只使用构造函数模式来定义类型。使用最多的继承模式是组合继承,这种模式使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。

寄生组合式继承,集寄生式继承和组合继承优点于一身,是实现基于类型继承的最有效方式。

1.5 函数表达式

函数声明具有函数声明提升,通过函数声明的函数,可以在任何地方调用。函数声明有函数声明提升,在执行代码之前会先读取函数声明。这就意味着可以发布函数声明放在调用它的函数后面。

函数表达式在使用前必须先赋值,否则会报错

闭包是指有权访问另一个函数作用域中的变量的函数。作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象,全局的变量对象一直存在,函数的变量对象包括函数内部声明的变量、arguments、函数等,在函数运行的时候才创建这个变量对象,函数执行完毕,函数局部活动对象就会被销毁。

事件处理程序的代码执行时,有权访问全局作用域中的任何代码

函数表达式只能在函数表达式执行完后面,才能被调用

把函数当成值来使用的情况下,都可以使用匿名函数

递归。递归函数是在一个函数通过名字调用自身的情况下构成的

function factorial(num) {
    if (num <= 1) {
        console.log(num);
    } else {
        console.log(num*factorial(num-1));
        return num*factorial(num-1);
    }
}
factorial(3);

//经典闭包访问
function createFunctions() {
    var result = new Array();

    for(var i=0; i< 10; i++){
        result[i] = function (num) {
            return function () {
                return num;
            }
        }(i);
    }
}
//要理解上面的代码,先从里面开始,

 return function () {
        return num;
    }
//上面返回了一个匿名函数,所有后面可以直接跟(i),传一个参数进来。

每个函数在被调用时都会自动取得两个特殊变量:this和arguments内部函数在搜索这两个变来功能时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。不过,把外部作用域中的this对象保存在一个闭包能够访问的变量里,就可以然个闭包访问该对象了。

你可能感兴趣的:(Javascript知识点汇总)