你所应该知道的JS特性

这算是我知道的唯一一种既能写前端又能写后端的语言,这么腻害的语言不会太简单,并且还是脚本语言更是有各种不可描述的用法,这只是一小部分…..

包装对象

基本类型以对象的方式去使用时,JavaScript会自动转换成对应的包装类型,相当于new了一个对象,内容和基本类型的内容一样,但是如果执行完毕后,这个临时对象会被销毁,所以如果再访问时候就是undefined
比如string类型是基本数据类型,但它可以调用length属性,也可以赋予属性,但是如果访问之前赋予的属性就会提示undefined

1
2
3
4
var str = "lalala";
str.length; // 6
str.t=3; //成功
str.t; //undefined

作用域

js中是没有块级({})作用域的,但是函数作用域是有的,如果单独定义代码块({}),定义在里面的变量在代码块外照样可以使用,定义在里面和外面并无区别,比如

1
2
3
4
5
6
7
8
for (var i = 0; i < 10; i++){
//TODO
}
//和下面的写法是完全一样的
var i = 0;
for (; i < 10; i++){
//TODO
}

但是啊,在ES6面试之后有了let,即开始有了块级作用域…..
还有,当我们在函数内定义局部变量的时候,有时会用var a=b=1这样来建立2个变量,如果这样写,a确实是局部变量,但b是全局变量,在函数外也是可以拿到的,所以,一定要分开写

语句

创建函数我们一般使用两种形式,直接定义fun或者赋给一个变量,直接定义的会被预处理,在定义之前调用也是可以的

1
2
3
4
show();
function show() {
console.log('lalala')
}

使用for...in的时候要注意,它遍历的顺序是不确定的,具体与引擎的实现有关
使用with会使引擎的优化变得困难,所以不建议使用,可以用变量来代替,甚至在严格模式下,禁用了with
如果想要在严格模式下执行,只需要在函数第一句或者js文件的第一句写上"use strict",如果老版本浏览器会被当成字符串忽略,做到了很好的向下兼容

对象

对象中定义的属性是无序的,并且每一个属性都有一个字符串key和对应的value,就算你输入的不是字符串类型,js也会帮你转换成字符串类型,这也就能解释的通为什么等价于a.sa['s']里面是字符串了
我们也稍微了解到过,对象一般都是有原型的,就是prototype,当我们通过new创建一个对象的时候,prototype就指向了函数定义的原型,当然函数原型中也有它的原型就是Object,一层一层Object的原型就是null了,就好比很多属性都有toString方法,这个就是从Object继承而来的,感觉有点说白明白,举个栗子

1
2
3
4
5
6
7
8
9
10
11
function foo() {}
// 在原型上定义一个属性
foo.prototype.x = 3;

var obj = new foo();
obj.a = 1;
obj.b = 2;

console.log(obj.a); // 1
console.log(obj.b); // 2
console.log(obj.x); // 3

这就叫原型链,我怎么感觉和继承重写似得…..需要注意的一点是使用in来判断某属性是否存在时,是会顺着原型链查找的
创建对象还可以使用var a = Object.create(null);这样的意思其实就是a的原型指向null,不过一般是传入的一个对象,也就是指定原型的指向吧(继承 :雾)

null与undefined

说来也比较奇怪,js中竟然有两个表示“无”的值,一切都是历史遗留问题
undefined和null在if语句中,都会被自动转为false,相等运算符(==)甚至直接报告两者相等。
平常的使用中,它们基本没啥区别,也没啥问题

1995年JavaScript诞生时,最初像Java一样,只设置了null作为表示”无”的值。
根据C语言的传统,null被设计成可以自动转为0。

null像Java里的一样,被当成一个对象,是的,null是一个对象,如果你用typeof(null)来查看返回的是Object
所以嘛,有人(Brendan Eich)就认为这样是不合理的,所以又设计出了undefined
JavaScript的最初版本是这样区分的:null是一个表示”无”的对象,转为数值时为0;undefined是一个表示”无”的原始值,转为数值时为NaN。
但是呢,在实践中证明这样是不可行的,所以后来也就…..
目前普遍认为的是:
null表示”没有对象”,即该处不应该有值。典型用法是:

作为函数的参数,表示该函数的参数不是对象。
作为对象原型链的终点。

undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义。典型用法是:

变量被声明了,但没有赋值时,就等于undefined。
调用函数时,应该提供的参数没有提供,该参数等于undefined。
对象没有赋值的属性,该属性的值为undefined。
函数没有返回值时,默认返回undefined。

undefined :表示未知的事物,啥也没有,无法想象。
null :有这么个概念,但是没东西 ,但是 null并非object,虽然 typeof null 的结果是 object

全局变量

一般来说,定义全局变量有三种方式
第一种就是直接var一个,不过不要在函数内,否则就是局部变量了(哦,对JS中是没有class概念的)
另一种是直接给标识符赋值,这样会隐式的声明了全局变量test。即使该语句是在一个function内,当该function被执行后test变成了全局变量。有人说这才是最正统的定义全局变量的方式
最后一种就是不使用var使用window,类似:window.x = 4
查看定义的全局变量可以使用for in window

1
2
3
4
5
6
// 判断有没有a1 a2 a3变量
for(a in window){
if(a=='a1'||a=='a2'||a=='a3'){
alert(a)
}
}

需要注意一点的是:通过var声明的变量无法删除(delete a1),所有浏览器表现一致。这在犀牛书上也有提到。

NaN类型

全局属性NaN表示 Not-A-Number 的值,所以说它是一个全局对象的属性,NaN属性的初始值就是NaN,和Number.NaN 的值一样。也就是说 NaN 是一种特殊的 Number 类型值

无穷大除以无穷大、给任意负数做开方运算 或者 算数运算符与不是数字或无法转换为数字的操作数一起使用时都将返回 NaN
如果 JavaScript 期望使用一个数字,它把给定的值将转换为数字(如果转换结果无意义的话将返回 NaN)。
举个栗子就是:Number('a');

首先全局的 isNaN() 函数不能严格判断输入值是否为 NaN。严格判断使用typeof value === 'number' && isNaN(value);
NaN 和任何对象做运算都会返回NaN,好玩的是 NaN != NaN 确实是这样,它谁都不等,包括自己

window对象

JavaScript由三部分组成:EMCAScript、DOM、BOM。
DOM是一个使程序和脚本有能力动态地访问和更新文档的内容、结构以及样式的平台和语言中立的接口。,而BOM定义了JavaScript可以进行操作的浏览器的各个功能部件的接口。
首先应该清楚的是两者皆为接口定义。

  • DOM是W3C的标准(所有浏览器公共遵守的标准)
  • BOM是各个浏览器厂商根据DOM在各自浏览器上的实现
  • window是BOM对象,而非JavaScript对象,不过恰好为EMCAScript中所定义的Global对象

window是BOM对象,而非JavaScript对象,不过恰好为EMCAScript中所定义的Global对象
由于window包含了document,因此JavaScript可以直接通过使用window的document对象来访问、检索、修改文档内容与结构。因为document对象又是DOM的根节点,所以可以理解为BOM包含了DOM。即浏览器提供出来给予访问的是BOM对象,而BOM对象再访问到DOM对象,从而js可以操作浏览器以及浏览器读取到的文档。

原文:https://www.zhihu.com/question/29917511

字符串的拼接

在很多高级语言中,加号(+)在字符串的操作中被赋予了更多的意义:作为字符串拼接的操作符。不过在Java和C#中,我们也知道如何频繁进行字符串拼接的操作,使用加号(+)就会产生效率问题,因此在这种情况下就会推荐使用StringBuilder。
我们知道 java 中 String 是引用类型,使用 += 进行字符串拼接将会频繁地分配新地址,指向新的地址块,这无疑白白地消耗了系统的性能。javascript中的字符串类型同java的String类似,如果我们大量使用+=进行字符串拼接的话,将会使界面失去响应(卡死状态)
解决方案可以使用数组来代替 StringBuilder:

1
2
3
4
5
6
7
8
9
10
11
12
var strArr = new Array();
strArr.push("aaaa");
strArr.push("bbbb");
strArr.push("ccccc");
alert(strArr.join(' '));

// 或者
var sb = [];
for(var i = 0; i <=21; i++) {
sb.push(i);
}
document.write(sb.join(''));

在其他语言中也是类似,所以频繁拼接的尽量避免 += 方式,比如 python 中可以使用 ''.join 来进行拼接
涉及到中文的字符记得使用 js 中的 encodeURL 方法进行编码.

箭头函数

ES6标准新增了一种新的函数:Arrow Function(箭头函数),它相当于是匿名函数,简化了函数的定义,有点 lambda 的味道~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
x => x * x
// 等价于
function (x) {
return x * x;
}

// 包含多条语句的
x => {
if (x > 0) {
return x * x;
}
else {
return - x * x;
}
}

// 两个参数:
(x, y) => x * x + y * y

// 无参数:
() => 3.14

// 可变参数:
(x, y, ...rest) => {
var i, sum = x + y;
for (i=0; i<rest.length; i++) {
sum += rest[i];
}
return sum;
}

// 返回一个对象,为了避免冲突使用括号
x => ({ foo: x })

箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。
回顾前面的例子,由于JavaScript函数对this绑定的错误处理,下面的例子无法得到预期结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = function () {
return new Date().getFullYear() - this.birth; // this指向window或undefined
};
return fn();
}
};

// 箭头函数方式
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
return fn();
}
};
obj.getAge(); // 25

由于this在箭头函数中已经按照词法作用域绑定了,所以,用call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略。

其他补充

定义正则用 /str/ ,如果是全文匹配就是 /str/g ,全文匹配表示匹配到一个后并不会停下来,它不需要转义,但是使用 new RegExp("\\d+") 这样的原始形式是需要转义的。

isNaN() 函数通常用于检测 parseFloat() 和 parseInt() 的结果,以判断它们表示的是否是合法的数字。

禁止事件冒泡: e.preventDefault(),IE 则是使用 e.returnValue = false; ,e 为事件 event,一般在函数中传递。
javascript 的 return false 只会阻止默认行为,而是用 jQuery 的话则既阻止默认行为又防止对象冒泡。

参考

http://www.ruanyifeng.com/blog/2014/03/undefined-vs-null.html

喜欢就请我吃包辣条吧!

评论框加载失败,无法访问 Disqus

你可能需要魔法上网~~