javaScript 基础
初学JavaScript基础
ASI
自动分号插入(ASI)的目标是使分号对行结束来来说是可选的。引入自动分号插入是为 了我们在自动解析插入分号的场景(对其内部来说,事情的处理通常是不一样的)。
ASI帮助解析器是确定语句的结束。通常情况下会是分号,ASI到如下情况会认为语句结束
- 行结束符后(如换行符),跟着非法token
- 遇到一个结束的花括号
- 文件已达结尾
ASI陷阱
return {
name:'John'
};
//会变成
return;
{
name:'John'
};
数字的方法调用
- 1..toString()
- 1 .toString()
- (1).toString()
- 1.0.toString()
严格模式
在ECMAScript5提供了严格模式。在严格模式中,JavaScript代码会更加简洁。会有更少不安全的特效。更多的警告和更加合理的代码。普通模式也被称为“宽松模式”启用严格模式,在JavaScript文件或者<\scrpte/>标签第一行加入"use strict"
也可以为每个函数开启严格模式
function foo(){
'use strict';
}
严格模式下的一些特点在严格模式中,所有变量都必须被显式声明,这有能防止拼写错误
//在宽松模式下
function sloopFunc(){
sloopVar=123;
}
sloopFunc();
console.log(sloopVar);
//严格模式下,未被声明的变量会抛出异常
function strictFunc(){
'use strict';
strictVar=123;
}
strictFunc()//ReferenceError:strictVar not defined
在严格模式下,所有函数必须在顶级作用域中声明(在全局或者直接在函数内)。
function strictFunc(){
'use strict';
{
//syntaxError:
function nested(){
}
}
}
//上面代码是无效的,因为函数需要被创建于包围函数的作用域中,而不是在快内
,如果要消除这个限制,可以通过一个变量声明和函数表达式在块中创建函数
function strictFunc(){
'use strict';
{
//OK:
var nested=function nested(){
}
}
}
在严格模式下 对函数参数的规定更加严格:参数禁止同名,因为会存在参数同名的局部变量arguments对象拥有跟少的属性,严格模式下arguments对象变得更加简单:arguments.callee和arguments.caller被取消,你不能指定arguments变量并且arguments不会跟踪参数的变化(当一个参数变化,对应的数组不会改变)无方法的函数中this的值为undefined在宽松模式下,在无方法的函数中this的值会指向全局函数(在浏览器中指的window)
function sloopyFunc(){
console.log(this===window);//true
}
//在严格模式下 它是undefined
function strictFunc(){
'use strict';
console.log(this===undefined)//true
}
对于构造函数,在严格模式中构造函数Point如下
function Point(x,y){
'use strict';
this.x=x;
this.y=y;
}
//由于严格模式下,如果不下心忘记了new并且调用了函数
var pt=Point(3,1);
//TypeError:Cannot set property 'x' of undefined,在宽松模式下不会报错,
并且会创建变量x和y
在严格模式下 设置或者删除不可改变的属性会抛出异常
var str='abc'
function sloopFunc(){
str.length=7;
console.log(str.length)//3,并不会改变
}
function strictFunc(){
'use strict';
str.length=6;//TypeError:cannot assign to read-only property
‘length'
}
在严格模式下被禁用的特性
- with不能被调用
- 没有八进制的数字;在宽松模式下,一个以0开头的数字会被解读为八进制的数字
010===8//true
undefined和null
JavaScript中有两个“空值",用来表示信息缺失,undefined和null
- undefined表示“没有值”(既不是原始值也不是对象)。访问未初始化的变量、缺失的参数、以及缺失的属性会返回这个空值。并且如果函数中没有任何显示的返回值时,则会隐式地返回undefined
- null的意思是“没有对象”。在用到对象的时候表示它空置(比如参数,对象链中的最后一个元素)出现的场景
undefined的场景
未初始化变量是undefined
var foo;
foo;//undefined
缺少参数
function f(x){return x}
f()//undefined
访问一个不存在的属性
var obj={}
obj.foo//undefined
如果函数没有显示地返回值,函数会隐式地返回undefined
function f(){}
f()//undefined
function g(){return ;}
g()//undefined
null场景
null 是原型链最顶端的元素
Object.getPrototypeOf(Object.prototype)//null
当字符串中没有匹配到正则表达式的结果时,RegExp.prototype.exec()会返回
null
/x/.exec('aaa')//null
检测undefined和null
严格相等检测
if(x===null)...
if(x===undefined)
//显示的监测
if(x!==undefined&&x!==null){
...
}
另一种检测方式利用undefined和null可被认为false
if(x){
....
}
//false,0,Nan,"" 也被认为false
undefined和null的历史
为什么JavaScript会有两个这样的值?这是有历史原因的
JavaScript采用了Java中将变量分为原始值和对象的处理方式。同时也使用了Java中表示"非对象"的值null。遵循C语言的先例,null在强制转化为数字时会变为0(java不会)
Number(null)//0
5+null//5
JavaScript在第一版的时候没有异常处理。因此在遇到为未初始化的变量和缺失的参数等异常情况时需要通过一个值来表示。null是一个很好的选择,但是Brendan Eich想要在这个时候避免两种情况
- 这个值不应该具有指向性,因为它表达的不仅仅是一个对象
- 这个值的强制转换不应该为0,因为这会使错误难以发现
因此加入undefined作为另一个空值他会强制转换为NaN
Number(undefined)//NaN
5+undefined//NaN
原始值的包装与去包装
当需要对一个原始值增加属性时,首先要对这个原始值进行包装并且给包装后的对象增加属性,而当你要使用值之前需要先对它进行去包装
原始值包装
new Boolean(true)
调用valueOf()去包装
new Boolean(true).valueOf()
将包装对象转化为原始值之时能正确提取数字和字符串,而布尔值不能。
Boolean(new Boolean(false))//true
Number(new Number(123))//123
String(new String('abc'))/abc
强制类型转换
大多数运算符、函数、方法会将调用的运算数和参数强制转换为它们需要的类型。例如
'3'*'4'//12
3+'times'//'3 times'
强制转换的bug
var formData={width:'100'}
var w=formData.width
var outer =w+20
console.log(outer===120)//false
console.log(outer==='10020')//true
ToPrimtive--将值转换为原始值
要将任意值转换为数字或字符串,首先会被转换为任意的原始值,然后再转发为最终的结果ECMAScript 规范中有一个内部函数,ToPrimitive(),能够实现这个功能。该函数是
ToPrimitive(input,PreferredType?)
可选参数,PreferredType表明转换后的类型:它可以是Number或String,具体取
决于ToPromitive的结果是希望转换为数字还是字符串
如果PreferredType 是Number 会执行的操作
- 如果input是原始值,返回这个值(没有其他需要做的)
- 否则,如果input是对象,调用input.valueOf()。如果结果是原始值,则返回结果
- 负责调用input.toString().如果结果是原始值,返回结果
- 否则,抛出一个TypeError
运算符
所以运算符都会强制要求他们的运算类型为合适的类型。大部分运算符只对原始值有效。如果是对象在运算之前会被转换为原始值。比如加号 在数组拼接 在js中它会将数组转化为字符串,然后将他们凭借起来
[1,2]+[3]//'1,23'
String([1,2])//'1,2'
String([3])//3
===和==
- 严格相等(===)很多严格不相等要求比较的值必须是相同的类型
- 宽松相等和不等会尝试将不同类型的值进行装换,在使用严格相等进行比较
宽松相等存在的问题
- 执行转换的过程令人困惑
- 由于运算符的宽容特效,使得类型错误可能长时间不能被发现
相等是不能自定义的,运算符在JavaScript中不能被重载,也不能自定义它的运算规则。
严格相等
不同类型的值总是严格不相等。如果两个值的类型相同,则会进行一下操作
- 是否为undefined===undefined的比较
- 是否为null===null的比较
- 。。。。
- NaN===NaN//false
宽松相等
如果两个运算数的类型相同,则使用严格相等比较,不相同,则一下判断
- undefined==null//true
- 字符串和数字对比,字符串转换为数字,然后严格相等比较
- 布尔值和非布尔值比较,则将布尔值转化为数字,在比较
- 对象和数字或字符串比较,将对象转化为原始值(ToPrimitive()),然后宽松比较
- 如果其他不符合,宽松比较为false
陷阱
宽松相等于布尔值装换不同
2==true//false//2===1
2==false//false//2===0
1==true//true//1===1
0===false//true//0===0
空字符串等于false
''==false//true//0===0
'1'==true//true//1===1
'2'==true//false//2===1
'abc'==true//false//NaN===1
宽松相等的字符串
'abc'==new String('abc')//true//'abc'=='abc'
'123'==123//true//123===123
'\n\t123\r '==123//true
''==0//o===0//true
宽松对象相等
如果比较对象,它们会被转换为原始值,将导致奇怪的结果
{}=={object object}//true
['123']==123//true
[]==0//true
运算符
3+1//4
3+true//4
'foo'+(1+2)//foo3
('foo'+1)+2//foo12
逗号运算符
逗号运算符会执行两边的运算数斌返回右边的结果
var x=0;
var y=(x++,10);
x=1;
y=10;
void 运算符
void 0//undefined
void (0)//undefined
void 4+7//NaN
void (4+7)//undefined
void 的好处
- void 0等同于undefined,后者可以被修改,而前者总会正确的值
- 避免表达式返回结果,如果某个表达式不需要,返回undefined很重要。void运算符可以避免这个结果,某个场景:JavaScript:URLs.它不会触发链接,这对书签很有用,当你访问这个url时,如果URL的结果是undefined,不少浏览器会替换当前文档内容为URL对于的内容。因此,如果想要打开一个新窗口而不改变当前显示的内容可以这样做
javascript::void window.open("http://xxxx")
- 一个IIFE 必须被解析成一个表达式添加void前缀
typeof和instanceof
- typeof 运算符能区分原始值和对象,并检测出原始值的类型
- Instanceof运算符可以检测一个对象是否特定构造函数的一个实例
运算符 | 结果 |
---|---|
undefined | undefined |
null | object |
布尔值 | boolean |
数字 | number |
字符串 | string |
函数 | function |
所有对象都是真值
Boolean(new Boolean(false))//true
逻辑运算符的应用
不同于java返回的是true或false 返回的是相应的对象
//参数默认值
function savText(text){
text=text||'';
}
//属性默认值
setTitle(options.title||'Untitled')
//函数结果返回值
function countOccurences(regex,str){
return (str.match(regex)||[]).length;
}
特殊的数字
JavaScript中有一些特殊的数字
- 两个错误值,NaN和Infinity
- 两个零值,+0和-0.
NaN的是not a number 的缩写,错误产生
Number('xyz')//NaN
Number('undefined')//NaN
失败的操作
Math.acos(2)//NaN
Math.sqrt(-1)//NaN
检查是否是NaN
NaN是唯一一个和自身不相等的值
NaN===NaN//false
使用全局函数isNaN()
isNaN(NaN)//true
isNaN(123)//false
isNaN对非数字不起作用
最好的办法
function myIsNan(value){
return typeof value==='number'&&isNaN(value);
}
或者检查这个值和他本事是否相等(因为NaN 是唯一有这样特征值)
function muIsNaN(Value){
return value!==value;
}
Infinity
Infinity是一个错误值。大到无法描述或者小道无法描述(除了和NaN比)用户数字的函数
- isFinite(number)//检查是否是一个实际的数,不是infinite和NaN
- isNaN
- parseFloat(str)//将str转化为浮点数
- parseInt(str,radix?)//将str解析成基于raix(2-36)的整数
字符串比较
运算符比较的缺点
- 区分大小写
- 不能很好的处理变音字符和重音符
使用String.prototype.localeCompare(other)//进行字符串比较(单有些浏览器不支持)
slicehe substring 区别
slice 返回一个子字符串,它包括原字符串从start开始到end 结束,包括(start,end),两个参数都可以是负数,负数回合字符串的length相加
'abc'.slice(2)//'c'
'abc'.slice(1,2)//b
'abc'.slice(-2)//bc
substring 可以替代slice,可以更好的跨浏览器兼容
不要用for-in 便利 数组,原因
- for in只会遍历索引,而不是数组元素
- for-in 还会遍历所以非索引的属性值
var arr=['a','b','c'];
for(var key in arr){console.log(key);}//0,1,2
var arr=['a','b','c'];
arr.foo=true
for(var key in arr){console.log(key);}//0,1,2,foo
for-in 会遍历所有(可枚举的)属性,其中包括继承来的属性。通常使用
hasOwnProperty来排除对象所继承的属性
finally和java的不同
finally在return 之前操作
var count=0;
function countUp(){
try{
return count;
}finally{
count++;
}
}
//输出为1,而java中输出是0
控制函数
所有的函数都具有call(),apply()和bind()方法。它们可以在执行方法时,用一个值指向this,并改变面向对象的作用域
func.apply(thisValue,argArray){
}
func(arg1,arg2,arg3)等价于
func.apply(null,[arg1,arg2,arg2])
func.bind(thisValue,arg1...argN)
这个方法会执行部分的函数功能,他会创建一个新的函数,这个函数会调用func,
并且将thisValue指定为this,同时应用一下参数:arg1直到argN,紧随着是新函
数的实际参数
IIFE
通过引用一个新的作用域来限制变量的生命周期。
function f(){
if(condition){
var tmp=...;
}
//tmp still exists here
}
function f(){
if(condition){
(function (){
}());
}
}
IIFE特点
- 它是立即执行的
- 必须是表达式
- 后面有封号
IIFE变体:前缀运算符
避免造成全局变量
<srcipt>
var temp=generateData();
processsData(temp);
persisData(temp);
</srcitpi>
//通过IIFE隐藏
<srcipt>
(function(){
var temp=generateData();
processsData(temp);
persisData(temp);
}());
</srcitpi>
闭包:使函数可以维持其创建时所在的作用域
如果一个函数离开了它被创建时的作用域,他还是会与这个作用域以及其外部的作用域的变量相关联
function createInc(startValue){
return function(step){
startValue+=step;
return startValue;
}
}
var inc=createInc(5);
inc(1);//6
inc(2)//8
闭包是一个函数外加上该函数创建时所建立的作用域。闭包的名字来源于闭包“关闭" 了一个函数中自由变量的访问权限。我们所说的变量是自由的,是指该变量是定义在函数外部的,而非函数内部。
个人博客http://blog.csdn.net/qq_22329521/article/details/52823598
内容来自深入理解JavaScript