【前端学习】JavaScript基础
数据类型篇
1. null是一个表示”空”的对象,转为数值时为0;undefined是一个表示“此处无定义”的原始值,转为数值时为NaN
2. JavaScript转换布尔值的规则是:除了undefined、null、false、0、NaN、空字符串转为false以外,其他值都视为true。空数组和空对象对应的布尔值都是true
3. JavaScript的Number精度最多只能到53个二进制位,它对15位的十进制数都可以精确处理
4. JavaScript能够表示的数值范围为21024到2-1023(开区间),超出这个范围的数无法表示
5. NaN不等于任何值,包括它本身。数组的indexOf方法内部使用的是严格相等运算符,所以该方法对NaN不成立
6. 判断NaN更可靠的方法是:利用NaN为唯一不等于自身的值的这个特点,进行判断
7. 对于码点在U+10000到U+10FFFF之间的字符,JavaScript总是认为它们是两个字符(length属性为2)。所以处理的时候,必须把这一点考虑在内。也就是说,JavaScript返回的字符串长度可能是不正确的
8. 为了避免歧义,JavaScript引擎在无法确定一行代码是对象还是代码块时,一律解释为代码块。如果要解释为对象,最好在大括号前加上圆括号
9. 只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除。它只能删除对象本身的属性,无法删除继承的属性。即使delete返回true,该属性依然可能读取到值
10. with语句一个很大的弊病是绑定对象不明确,建议不要使用,可以考虑用一个临时变量代替with
11. 采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效
12. 函数的作用域与变量一样,就是其声明时所在的作用域,与运行时所在的作用域无关
13. 函数的length属性与实际传入的参数个数无关,只反映函数预期传入的参数个数。但是,没有办法只省略靠前的参数,而保留靠后的参数。如果一定要省略靠前的参数,只有显式传入undefined
14. 函数参数如果是原始类型的值,传递方式是传值传递。但是,如果函数参数是复合类型的值,传递方式是传址传递
15. 如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这是将不会影响到原始值
16. 如果有同名的参数,则取最后出现的那个值
17. arguments对象只有在函数体内部才可以使用,且可以在运行时修改。通过arguments对象的length属性,可以判断函数调用时到底带几个参数
18. 立即调用的函数表达式:(function(){ /* code */ }());
19. 数组的数字键不需要连续,length属性的值总是比最大的那个整数键大1
20. 使用delete命令删除一个数组成员,会形成空位,并且不会影响length属性
21. 如果是空位,使用数组的forEach方法、for...in结构以及Object.keys方法进行遍历,空位都会被跳过。如果某个位置是undefined,遍历的时候就不会被跳过
运算符
22. 加法运算符存在重载,可能执行两种运算,使用的时候必须很小心
23. 可以自定义对象的valueOf方法或toString方法,得到想要的结果
24. 如果运算子是一个Date对象的实例,那么会优先执行toString方法
25. 为了得到负数的正确余数值,可以先使用绝对值函数
26. 对于两个对象的比较,严格相等运算符比较的是地址,而大于或小于运算符比较的是值
27. 如果对一个值连续做两次取反运算,等于将其转为对应的布尔值,与Boolean函数的作用相同,这是一种常用的类型转换的写法
28. 且运算符可以多个连用,这时返回第一个布尔值为false的表达式的值。如果所有表达式的布尔值都为true,则返回最后一个表达式的值
29. 或运算符可以多个连用,这时返回第一个布尔值为true的表达式的值。如果所有表达式都为false,则返回最后一个表达式的值
30. 对取反运算,可以简单记忆成:一个数与自身的取反值相加,等于-1
31. 对一个小数连续进行两次二进制否运算,能达到取整效果,使用二进制否运算取整,是所有取整方法中最快的一种
32. 使用异或运算可以在不引入临时变量的前提下,互换两个变量的值,使用方法:a ^= b, b ^= a, a ^= b
33. 左移即将一个数乘以2的指定次方。向左移动的时候,最高位的符号是一起移动的。它用于二进制数值非常方便,下面是一个将颜色的RGB值转为HEX值的例子:
var color = {r: 186, g: 218, b: 85};
// RGB to HEX
// (1 << 24)的作用为保证结果是6位数
var rgb2hex = function (r, g, b) {
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b)
.toString(16) // 先转成十六进制,然后返回字符串
.substr(1); // 去除字符串的最高位,返回后面六个字符串
}
rgb2hex(color.r, color.g, color.b)
// "#bada55"
34. 右移运算符基本上相当于除以2的指定次方(最高位即符号位参与移动)
35. 查看一个负整数在计算机内部的储存形式,最快的办法是采用头部补零的右移运算符,如-1 >>> 0会得到4294967295
36. void运算符的作用是执行一个表达式,然后不返回任何值,或者说返回undefined。这个运算符的主要用途是浏览器的书签工具,以及在超级链接中插入代码防止网页跳转。一个实际的例子如下:
<a href="javascript: void(document.form.submit())">
提交
</a>
37. 赋值运算符(=)和三元条件运算符(?:)是右结合的。除此之外,指数运算符(**)也是右结合的
语法专题
38. 使用Boolean()进行布尔值转换时,所有对象(包括空对象)的转换结果都是true,甚至连false对应的布尔对象new Boolean(false)也是true
39. return语句的执行是排在finally代码之前,只是等finally代码执行完毕后才返回
40. 建议可以用空格区分两种不同的圆括号(表示函数的调用以及表达式组合的),具体规则如下:
1. 表示函数调用时,函数名与左括号之间没有空格
2. 表示函数定义时,函数名与左括号之间没有空格
3. 其他情况时,前面位置的语法元素与左括号之间,都有一个空格
41. 如果不得不使用全局变量,可以考虑用大写字母表示变量名,这样更容易看出这是全局变量,比如UPPER_CASE
42. 建议switch...case结构可以用对象结构代替,如下:
function doAction(action) {
var actions = {
'hack': function () {
return 'hack';
},
'slash': function () {
return 'slash';
},
'run': function () {
return 'run';
}
};
if (typeof actions[action] !== 'function') {
throw new Error('Invalid action.');
}
return actions[action]();
}
43. console.dir()方法用来对一个对象进行检查(inspect),并以易于阅读和打印的格式显示
标准库
44. Object对象的原生方法分成两类:Object本身的方法与Object的实例方法。所谓“本身的方法”就是直接定义在Object对象的方法,所谓实例方法就是定义在Object原型对象Object.prototype上的方法。凡是定义在Object.prototype对象上面的属性和方法,将被所有实例对象共享
45. 如果参数是原始类型的值,Object方法将其转为对应的包装对象的实例。如果Object方法的参数是一个对象,它总是返回该对象,即不用转换。利用这一点,可以写一个判断变量是否为对象的函数:
function isObject(value) {
return value === Object(value);
}
isObject([]) // true
isObject(true) // false
46. 通过var obj = new Object()的写法生成新对象,与字面量的写法var obj = {}是等价的。或者说,后者只是前者的一种简便写法
47. 通过自定义对象的valueOf方法,可以得到想要的结果,例子如下:
var obj = new Object();
obj.valueOf = function () {
return 2;
};
1 + obj // 3
48. 利用Object.prototype.toString,可以写出一个比typeof运算符更准确的类型判断函数:
var type = function (o){
var s = Object.prototype.toString.call(o);
return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};
49. Object.keys只返回对象自身的可遍历属性的全部属性名,而Obj.getOwnPropertyNames会将它们都返回
50. 一旦定义了取值函数get(或存值函数set),就不能将writable属性设为true,或者同时定义value属性,否则会报错
51. 如果原型对象的某个属性的writable为false,那么子对象将无法自定义这个属性。但是,有一个规避方法,就是通过覆盖属性描述对象,绕过这个限制
52. 可配置性决定了目标属性是否可以被删除
53. 取值函数与存值函数的一个例子如下:
var obj = {
$n : 5,
get next() { return this.$n++ },
set next(n) {
if (n >= this.$n) this.$n = n;
else throw new Error('新的值必须大于当前值');
}
};
obj.next // 5
obj.next = 10;
obj.next // 10
obj.next = 5;
// Uncaught Error: 新的值必须大于当前值
54. shift()方法用于删除数组的第一个元素,并返回该元素;unshift()方法用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度
55. 用于数组合并的concat方法返回的是一个新数组,原数组不变
56. 如果数组遍历的目的是为了得到返回值,那么使用map方法,否则使用forEach方法
57. reduce方法和reduceRight方法依次处理数组的每个成员,最终累计为一个值。它们的差别是,reduce是从左到右处理,reduceRight则是从右到左,其他完全一样
58. 某些场合,原始类型的值会自动当作包装对象调用,即调用包装对象的属性和方法。这时,JavaScript引擎会自动将原始类型的值转为包装对象实例,并在使用后立刻销毁实例
59. 由于浮点数的原因,小数5的四舍五入是不确定的,使用的时候必须小心
60. 字符串对象是一个类似数组的对象(很像数组,但不是数组)
61. 新建正则表达式有两种方法,但基本上都采用字面量定义正则表达式
62. 正则实例对象的test方法返回一个布尔值,表示当前模式是否能匹配参数字符串
63. 如果参数对象有自定义的toJSON方法,那么JSON.stringify会使用这个方法的返回值作为参数,而忽略原对象的其他属性
面向对象编程
64. 应该非常小心,避免不使用new命令,直接调用构造函数
65. 使用new命令时,它后面的函数依次执行下面的步骤:
1. 创建一个空对象,作为将要返回的对象实例
2. 将这个空对象的原型,指向构造函数的prototype属性
3. 将这个空对象赋值给函数内部的this关键字
4. 开始执行构造函数内部的代码
66. 如果构造函数内部有return语句,而且return后面跟着一个对象,new命令会返回return语句指定的对象;否则,就会不管return语句,返回this对象
67. 函数内部可以使用new.target属性,如果当前函数是new命令调用,new.target指向当前函数,否则为undefined
68. 想以现有对象作为模板,生成新的实例对象,可以使用Object.create()方法
69. 简单说,this就是属性或方法“当前”所在的对象
70. 下面这几种用法,都会改变this的指向:
var obj = {
foo: function () {
console.log(this);
}
};
obj.foo() // obj
(obj.foo = obj.foo)() // window
(false || obj.foo)() // window
(1, obj.foo)() // window
// 上面代码中,obj.foo就是一个值
// 这个值真正调用的时候,运行环境已经不是obj了,而是全局环境,所以this不再指向obj
71. 使用一个变量固定this的值,然后内层函数调用这个变量,是非常常见的做法,请务必掌握。下面是这个做法的一个例子:
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = function() {
console.log(that);
}();
}
}
o.f1()
// Object
// Object
72. 函数实例的call方法,可以指定函数内部this的指向,然后在所指定的作用域中,调用该函数
73. 利用数组对象的slice方法,可以将一个类似数组的对象(比如arguments对象)转为真正的数组
74. bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数
75. 将Function.prototype.call方法绑定到Function.prototype.bind对象,就意味着bind的调用形式也可以被改写:
function f() {
console.log(this.v);
}
var o = { v: 123 };
var bind = Function.prototype.call.bind(Function.prototype.bind);
bind(f, o)() // 123
76. 如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)
77. prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。它的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的
78. 有了constructor属性,就可以从一个实例对象新建另一个实例
79. 修改原型对象时,一般要同时修改constructor属性的指向,如下所示:
// 坏的写法
C.prototype = {
method1: function (...) { ... },
// ...
};
// 好的写法
C.prototype = {
constructor: C,
method1: function (...) { ... },
// ...
};
// 更好的写法
C.prototype.method1 = function (...) { ... };
80. instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例
81. 让一个构造函数继承另一个构造函数,是非常常见的需求。这可以分成两步实现:
// 第一步:在子类构造函数中,调用父类构造函数
function Sub(value) {
Super.call(this);
this.prop = value;
}
// 第二步:让子类的原型指向父类的原型,这样子类就可以继承父类原型
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.method = '...';
// 另一种写法是Sub.prototype等于一个父类实例
Sub.prototype = new Super();
// 这种写法也有继承的效果,但是子类会具有父类实例的方法,不推荐
82. 可通过变通方法实现多重继承的功能。这种模式又称为Mixin(混入):
function M1() {
this.hello = 'hello';
}
function M2() {
this.world = 'world';
}
function S() {
M1.call(this);
M2.call(this);
}
// 继承M1
S.prototype = Object.create(M1.prototype);
// 继承链上加入M2
Object.assign(S.prototype, M2.prototype);
// 指定构造函数
S.prototype.constructor = S;
var s = new S();
s.hello // 'hello'
s.world // 'world'
83. hasOwnProperty方法是JavaScript之中唯一一个处理对象属性时,不会遍历原型链的方法
84. 在严格模式中,函数内部改变参数与arguments的联系被切断了,两者不再存在联动关系
异步操作
85. 我们可以编写一个流程控制函数,让它来控制异步任务,一个任务完成以后,再执行另一个:
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
function async(arg, callback) {
console.log('参数为 ' + arg + ' , 1秒后返回结果');
setTimeout(function () { callback (arg * 2) }, 1000);
}
function final(value) {
console.log('完成:', value);
}
function series(item) {
if(item) {
async( item, function(result) {
results.push(result);
return series(items.shift());
});
} else {
return final(results[results.length - 1]);
}
}
series(items.shift());
86. 流程控制函数也可以是并行执行,即所有异步任务同时执行,等到全部完成后,才执行final函数:
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
function async(arg, callback) {
console.log('参数为 ' + arg + ' , 1秒后返回结果');
setTimeout(function () { callback (arg * 2); }, 1000);
}
function final(value) {
console.log('完成:', value);
}
items.forEach(function(item) {
async(item, function(result) {
results.push(result);
if(results.length === items.length) {
final(results[results.length - 1]);
}
})
});
87. 如果回调函数是对象的方法,那么setTimeout使得方法内部的this关键字指向全局环境,而不是定义时所在的那个对象
88. setTimeout(f, 0)的一大应用是可以调整事件的发生顺序。计算量大、耗时长的任务,常常会被放到几个小部分,分别放到setTimeout(f, 0)里面执行
89. Promise对象的报错具有传递性
90. then()有四种用法,它们的差别如下:、
// 写法一
f1().then(function () {
return f2();
}).then(f3);
// f3回调函数的参数是f2函数的运行结果
// 写法二
f1().then(function () {
f2();
}).then(f3);
// f3回调函数的参数是undefined
// 写法三
f1().then(f2()).then(f3);
// f3回调参数的参数是f2函数返回的函数运行结果
// 写法四
f1().then(f2).then(f3);
// f2会接收到f1()返回的结果
浏览器模型
91. AJAX只能向同源网址(协议、域名、端口都相同)发出HTTP请求,如果发出跨域请求,就会报错
92. 如果需要跨域AJAX请求发送Cookie,需要withCredentials属性设为true
93. 浏览器安全的基石是“同源政策”(same-origin policy)
94. CORS是一个W3C标准,全称是“跨域资源共享”(Cross-origin resource sharing),它允许浏览器向跨域的服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制
95. ArrayBuffer对象表示一段二进制数据,用来模拟内存里面的数据。通过这个对象,JavaScript可以读写二进制数据。这个对象可以看作内存数据的表达
96. 用户上传文件时,表单<form>元素的method需要设置为POST,enctype属性设为multipart/form-data