关于省略分号的换行
javascript在解析时,会把省略分号的行与下一行一起解析,当不构成语法错误时回合为一行,因此类库的保守写法,会在已开始前添加分号,以防出现上述问题。
关于分数的精度问题
javascript可以精确地表示分数1/2、1/8等,通常所用的1/10、1/100不能被精确地表示出来。
var x=.3-.2;
var y=.2-.1;
x==y //false
x==.1 //false
y==.1 //true
解决方案,当进行相关计算可以换算成整数计算,最后使用科学计数法或单位换算进行结果的应用。
关于进制的问题
严格模式下禁止8禁止模式。ECMAscript不支持八进制的直接量。
多行字符串
JS中多遇到拼接字符串的问题,通常情况下会拼接一个超长字符串在一行,使文档可读性下降,可以使用换行符,使得一个字符串可以在多行写出。
在ECMAscript 3 中,字符串必须写在一行,但是在ESMAscript5 中字符串可以被拆成多行,每行一反斜线()结束,反斜线和行结束符都不算是字符串直接量的内容。如果希望再字符串直接量中另起一行,可以使用转义字符\n
"on
\line"
//输出为online
关于转义字符
反斜杠使我们避免使用常规方式解释单引号,当单引号不是用来标记字符串结尾时,他只是一个‘。
ESMAScript5,允许在一个多行字符串直接量里的每行结束处使用反斜杠。
You\'re right, it can\'t be a quote.
关于模式匹配
尽管RegExp并不是语言中的基本数据类型,但是他们依然具有直接量写法,可以直接在javascript程序中使用。在两条斜线之间的文本它构成了一个正则表达式直接量。第二条斜线之后也可以跟随一个或多个字母,用来修饰匹配模式的含义
var text = "testing:1,2,3";
var pattern=/\d+/g
partten.test(text) // 匹配成功
text.search(pattern) //9 首次匹配成功的位置
text.match(pattern) //["1","2","3"]所有匹配组成的数组
text.replace(pattern,"#"); //"testing:#,#,#"
text.split(/\D+/);//["","1","2","3"]用非数字字符截取字符串
关于全局对象
全局属性: undefined infinity NaN
全局函数: isNaN() parseInt() eval()
构造函数 : Date() RegExp() String() Object() Array()
全局对象:Math JSON
关于包装对象
对于string、bollen、num为什么会有属性呢,只要应用了字符串的属性,javascript就会将字符串值通过调用new String(s)的方式注册能换成为对象,这个对象继承了字符串的方法。并被用来处理属性的引用,一旦属性引用结束,这个新创建的对象就会销毁。
当读取字符串、数字、和布尔值的属性值的时候,表现的想对象一样,但是如果你试图给属性赋值,则会忽略这个操作;修改只是发生在临时对象上,而这个临时对象并没有被保留下来。
- 存取字符串和数字布尔值的属性时创建的临时对象称为包装对象。
- 字符串数字和布尔值的属性值是只读的,并且不能定义新属性。
关于对象的转换
对象到字符串
- 调用toString()方法,把返回值进行字符串的隐式转换。
- 没有toString()方法,调用valueOf(),并隐式转换返回值。
- 否则抛出类型异常
对象到数字
- 对象具有valueOf()方法,把返回值进行数字的隐性转换。
- 没有valueOf()方法,调用toString()方法,把返回值进行数字的隐形转换
- 否则抛出类型异常
空数组返回0的原因:
数组继承了默认的valueOf()方法,这个方法返回一个对象而不是一个原始值,因此数组到数字的转换则调用toString方法。空数组转换成为空字符串,空字符串转换为数字0.
关于作为属性的变量
var trunevar = 1; //声明一个不可删除的全局变量
fakevar = 2;//创建全局对象的一个可删除的属性
this.fakevar2 = 3;
delete trunevar //false
delete fakevar //true
delete this.fakevar2 //true
关于运算符
运算要求操作的数是整数,这些整数表示32位整型
按位非:
~ox0F = oxFFFFFFF0 表示为-16
左移和右移的位数是0~31之间的一个整数
带符号右移:
右边溢出将忽略,左边按照原有操作数符号来补位,补成一致的。
无符号右移:
最高位总是由0填补
-1>>>4=ox0FFFFFFF
关于相等和严格相等
严格相等:
- 明确类型不进行类型转换
- null ===null //false
- undefined ===undefined //false
- NaN === NaN //false
相等:
- null ==undefined //true
- '1' == true //true
关于逻辑非
恒等式
!(p&&q) === !p||!q
!(p||q) === !p&&!q
关于eval()
var geval = eval;
var x ='global',y='global';
function f(){
var x='local';
eval("x+='change';");//改变局部变量
return x;
}
function g(){
var y='local';
geval("y+='change';");//改变全局变量
return y;
}
console.log(f(),x); //"local changeglobal"
console.log(g(),y); //"local globalchange"
关于 typeof
数据类型 | 计算值 |
---|---|
NaN | number |
函数 | function |
宿主对象 | 由编译器各自实现的字符串 |
本地对象、内置对象和 宿主对象
1、本地对象
ECMA-262 把本地对象(native object)定义为“独立于宿主环境的 ECMAScript 实现提供的对象”。
再来看一下,“本地对象”包含哪些内容:
Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError
由此可以看出,简单来说,本地对象就是 ECMA-262 定义的类(引用类型)。
2、内置对象
ECMA-262 把内置对象(built-in object)定义为“由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现”。这意味着开发者不必明确实例化内置对象,它已被实例化了。
同样是“独立于宿主环境”。根据定义我们似乎很难分清“内置对象”与“本地对象”的区别。而ECMA-262 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)。
如此就可以理解了。内置对象是本地对象的一种。而其包含的两种对象中,Math对象我们经常用到,可这个Global对象是啥东西呢?
Global 对象是ECMAScript中最特别的对象,因为实际上它根本不存在,但大家要清楚,在ECMAScript中,不存在独立的函数,所有函数都必须是某个 对象的方法。类似于isNaN()、parseInt()和parseFloat()方法等,看起来都是函数,而实际上,它们都是Global对象的方 法。而且Global对象的方法还不止这些。有关Global对象的具体方法和属性,感兴趣的同学可以看一下这里:JavaScript 全局对象参考手册
3、 宿主对象
由ECMAScript实现的宿主环境提供的对象,可以理解为:浏览器提供的对象。所有的BOM和DOM都是宿主对象。
关于void 运算符
出现在操作数之前,操作数可以是任意类型。void会忽略操作数的值,因此在操作数据有副作用的时候使用void来让程序更具有语意。
运算符用于客户端的URL——javascript:URL中,在URL中可以写带有副作用的表达式,而void则让浏览器不必显示这个表达式的计算结果
打开一个新的窗口
关于switch
- 查找case中的表达式和expression的值全等于(===)进行匹配,表达式和case不会做模式的转换。
- 如果匹配不成功的话会执行default,如果没有default则会跳过所有的代码块。
- case只表明了起点,并没有标明终点,因此需要使用break使解释器跳出switch语句或循环语句,否则程序将执行下一个case。
关于循环
循环变量多是数字,下例为使用for循环来遍历链表数据结构,并返回链表中的最后一个对象。
function tail(o) {
for( ; o.next; o=o.next)
return o
}
关于for in 循环
for(variable in object)
statement
- 解释器先计算object表达式,如果表达式为null或者undefined,javascript解释器回调过循环并执行后续代码。
- 每次循环之前,都会计算variable表达式的值,并将属性名复制给他。
break&containue
break labelname //适用于嵌套多层的循环
containue labelname //适用于嵌套的循环中,用以跳出多层次嵌套的循环体逻辑
当break和标签一起使用的时候,程序将跳转到这个标签所表示的语句块的结束,或者直接终止这个闭合语句块的执行。
try/catch/finally
try{
//通常来讲,这里的代码不会出现问题
//有时抛出一个异常,要么由throw语句直接抛出异常
//要么通过调用一个方法间接抛出异常
}catch (e){
//当且仅当try语句抛出异常,才会执行的代码
//这里可以通过局部变量e来获取error对象或者抛出的其他值引用
//这里的代码可以给予某种原因处理这个异常
//可以通过throw重新抛出异常
}finally{
//不管try语句块是否抛出异常,这里的逻辑总是会执行的,终止try语句块的方式有
//正常终止,执行完语句块的最后一条语句
//通过break、containeu、return语句终止
//抛出一个异常,异常被catch从句捕捉
//抛出一个异常,异常未被捕获,继续向上传播
}
try中产生的异常没有catch进行处理,就会先执行finally之后,然后向上传播这个异常,知道找到能处理这个异常的catch从句。
关于严格模式
- 禁止使用with
- 所有变量需要声明
- 调用函数中的this值为undefined,可以使用这个特性来检测是否为严格模式
var hasStrictMode = (function(){
"use strict";
return this === undefined
}());
关于检测对象
检测集合中成员的所属关系——判断某个属性是否存在于某个对象中。
可以通过in运算符、hasOwnPreperty()和propertyIsEnumerable()方法来完成这个工作,甚至仅通过属性查找也可以得到结果。
propertyIsEnumer(),当属性为自有的并且是可枚举的,才会返回true。
检测P是否是O的原型
` p.isPrototypeOf(o);
关于序列化对象
对象序列化是指将对象转换为JSON格式的字符串。
JSON.stringify() //把对象撞换为字符串
JSON.parse() //把JSON格式字符串转换为对象
支持对象、数组、字符串、无穷大数字、bollen和null。
函数、RegExp、Error、undefined不能被序列化和还原。
NaN、Infinity的序列化结果是null。
关于toString()与toLocalString方法
在需要将对象转换为字符串的时候,javaScript会调用这个方法。比如说遇上+时。默认的该方法对于监测对象十分有用。
var s = {x:1,y:2}.toString(); //返回的是[Object,Object]
- Object的toLocalString方法,只是调用了toString而已。
- Date和Number对toLocalString方法做了定制,可以用它对数字日期和时间做本地化的转换
- 数组元素的tolocalstring方法和toString方法很像,唯一的不同是每个数组元素会调用各自的tolocalstring完成字符串的转换,而不是点用各自的tostring方法。
关于数组的声明
var udf = [,,] //数组的长度为2,原因是最后一个逗号可以省略也可以加上
关于稀疏数组
只包含从0开始的不连续索引的数组,即数组的长度大于数组元素的个数。
数组的长度
数组的长度是可配置的,通过改变数组的长度,删掉相关的索引值。使得数组相关的push和shift方法不能使用。
数组的相关操作
Slice
当出现负数的时候就是用数组的长度加上相应的负数,接收两个参数,第一个参数表示复制的起点,第二个表示终点。
splics
接收三个参数,操作位置,删除个数,插入元素,与concat不同,该方法接収元素而非数组
for-each
function foreach(a,f,t){
try{ a.forEach(f,t)}
catch(e) {
if( e=== foreach.break) return;
else throw e;
}
}
foreach.break = new Error ("stopIteration");
reduce&reduceRight
接受两个参数分别是在每一项上调用的函数和作为归并基础值。
提供给内函数的四个参数分别是,此项,下一项,此项索引,以及数组。
检测数组
Array.isArray() //ECMAScript5之前
instanceof 不准
//兼容3
var isArray = function.isArray || fucntion(o){
return typeof o ===="object" &&
Object.prototype.toString.call(o) === "[object Array]";
};
关于类数组
类数组的条件:
- 自动更新length属性
- 设置length为一个较小值将截断数组
- 从Array.prototype中继承一些有用的方法
- 其类属性为Array
function isArrayLike(o) {
if(o &&
typeof o === 'object' &&
isFinite(o.length) &&
o.length >= 0 &&
o.length === Math.floor(o.length) &&
o.length < 4294967296++)
return true
else
return false
}
类数组的操作
var a={'0':a, '1':b, '2':c, length:3};
Array.prototype.join.call(a,"+")
Array.prototype.map.call(a,function(x) {
return x.toUpperCase();
}) //["A","B","C"]
关于作为数组操作的字符串
字符串是不可变值的,只是可读的,所以使用push、sort、reverse、splice等方法是无效的,BUT不会报错。
关于数组的操作的分类
不改变数组值的 | 改变数组的值 |
---|---|
forEach()等迭代方法 | 栈、队列方法 |
reduce、reduceRight归并方法 | reverse |
concat连接方法 | splice |
slice | ----- |
关于函数的概念
- 形参相当于函数中定义的变量,实参是调用函数时传入的参数。
- javaScript中的函数是参数化的,函数定义的时候会包含 一个称为形参的标示符列表,这些参数在函数体重向局部变量一样工作。
- 每次调用还会拥有另一个值,本次调用的上下文——this。
- 如果通过访问对象方法调用函数,this指向该对象。
- javascript 的函数嵌套在其他函数中定义,他们就可以访问他们被定义时所处的作用域中的任何变量,这意味这闭包。
关于函数定义
函数名称--name
function name (arguments){
coments
}
表达式定义函数只适用于它作为一个大的表达式的一部分,比如在赋值和调用过程中定义函数。函数表达式定义函数之前无法调用函数,没有函数声明提升的过程。
如果一个函数定义表达式包含名称,函数的局部作用域将会包含一个绑定到函数对象的名称,函数名称将成为函数内部的一个局部变量。
var f = function fact(x){
if(x <= 1)
return 1;
else
return x*fact(x-1); //在函数外部将不能访问fact
}
fact(5); //fact is not defined
关于函数中的this
- 当函数作为方法调用,this指向的是拥有该方法的对象。
- 当函数作为函数调用,this指向的是全局对象,严格模式下为undefined。
- 当函数是构造函数,this指向构造函数新创建的对象。
- this不存在于作用域中,无法通过闭包的方式访问,如需访问可以把this的值付给你一个变量,使其存在于作用域中。
- this无法被赋值。
构造函数的return
- 构造函数一般没有return
- 显示使用return 返回一个对象,这个对象会代替构造函数创建的对象复制给new操作符左侧的变量。
- 如果return 为一个空语句或者返回值为原始值,那么这时将被忽略,同时把新生成的对象返回。
关于函数参数
- 对于有需要的实参进行判断和赋值,对于可选参数使用/optional/来表示。
- 对于不定实参函数,实参个数不能为零。
- Argument.callee指向正在执行的函数,可以使用这个来进行函数内部的解耦。
- 使用传入对象代替零散的参数,避免多参数函数调用时参数位置与参数对应的困难,提升语义性。
- arguments.length (实际传入的实参个数)
- arguments.callee.length(期望传入的实参个数)
关于用作命名空间的函数
利用作用域关系,定义一个函数用作临时的命名空间,在这个命名空间里定义的变量不会污染全局作用域。
(function(){
//coding
}());
第一个左侧括号是必须的,否则解释器会试图将关键字function 解析为函数声明语句。
关于闭包
- 同一个作用域链中定义的两个闭包,这两个闭包共享同样的私有变量。
function People(name){
this.name = name;
this.sayName = function(){name+=' hello';return name};
this.sayNameAgain = function(){return name};
};
var lixinyue = new People('lixinyue');
lixinyue.sayName(); //lixinyue hello
lixinyue.sayNameAgain();//lixinyue hello
lixinyue.sayName();//lixinyue hello hello
lixinyue.sayNameAgain();//lixinyue hello hello
lixinyue.name //lixinyue
关联到闭包的作用域都是活动的,记住这一点非常重要。嵌套函数不会将作用域内的私有成员复制一份,也不会对绑定的变量生成静态快照。
闭包无法访问父级函数this和arguments,如果有这个需要在定义父级函数时就要将this和arguments赋给一个变量保存起来。
关于使用call
和apply
- call和apply的第一个实参是要调用函数的母对象
- 其后传入的参数数组可以是真实数组也可以是类数组
关于bind()
- bind方法不仅仅是将函数绑定至一个对象,他还把传入的参数绑定到this。可以理解为把函数的形参在用函数内用绑定的值进行赋值。
- 因此bind返回的函数length,是原函数对象的形参个数减去bind绑定的实参个数。
- 当返回函数用于构造函数时,其所创建的对象从原始的未绑定的函数对象中继承prototype。
this 指针
- 函数在被调用时,this指针是由调用者所决定的。
- 直接调用时,this指针为Globel Object,浏览器端为window。
- 方法调用时,this指针为方法所属对象。如a.b.c(),this指针为a.b。
- 构造器调用时,解析器会创建一个Object,this指针为这个Object,默认这个Object会被返回
apply或call可以通过第一个参数指定调用时的this指针。