相等运算符==
与===
==
,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;
===
,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。
由于JavaScript这个设计缺陷,不要使用==
比较,始终坚持使用===
比较。
NaN
NaN
这个特殊的Number
与所有其他值都不相等,包括它自己:
NaN === NaN; // false
唯一能判断NaN
的方法是通过isNaN()
函数:
数组
JavaScript的数组可以包括任意数据类型,如:
[1, 2, 3.14, 'Hello', null, true];
创建数组的方法:
1. var arr = [1, 2, 3.14, 'Hello', null, true];
2. new Array(1, 2, 3); // 创建了数组[1, 2, 3]
变量
使用=
对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用var
申明一次.
var a = 123; // a的值是整数123
a = 'ABC'; // a变为字符串
多行字符串
由于多行字符串用\n
写起来比较费事,所以最新的ES6标准新增了一种多行字符串的表示方法,用反引号 `` 表示
模板字符串
var message = '你好, ' + name + ', 你今年' + age + '岁了!';
var message = `你好, ${name}, 你今年${age}岁了!`;
复制数组
如果不给slice()
传递任何参数,它就会从头到尾截取所有元素。利用这一点,我们可以很容易地复制一个Array
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
var aCopy = arr.slice();
aCopy; // ['A', 'B', 'C', 'D', 'E', 'F', 'G']
aCopy === arr; // false
给对象删除属性
delete xiaoming.age; // 删除age属性
判断对象是否拥有属性
如果要检测xiaoming
是否拥有某一属性,可以用in
操作符,如果in
判断一个属性存在,这个属性不一定是xiaoming
的,它可能是xiaoming
继承得到的
var xiaoming = {
name: '小明',
birth: 1990,
school: 'No.1 Middle School',
height: 1.70,
weight: 65,
score: null
};
'name' in xiaoming; // true
'grade' in xiaoming; // false
要判断一个属性是否是xiaoming
自身拥有的,而不是继承得到的,可以用hasOwnProperty()
方法:
var xiaoming = {
name: '小明'
};
xiaoming.hasOwnProperty('name'); // true
xiaoming.hasOwnProperty('toString'); // false
JavaScript把null
、undefined
、0
、NaN
和空字符串` `;视为false
,其他值一概视为true
循环
for … in
for
循环的一个变体是for ... in
循环,它可以把一个对象的所有属性依次循环出来
请注意,for … in对Array的循环得到的是String而不是Number。
Map和Set
初始化Map
需要一个二维数组,或者直接初始化一个空Map
。
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
要创建一个Set
,需要提供一个Array
作为输入,或者直接创建一个空Set
:
var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3
iterable
Array
、Map
和Set
都属于iterable
类型。
具有iterable
类型的集合可以通过新的for ... of
循环来遍历。
var a = [1, 2, 3];
for (var x of a) {
}
console.log('你的浏览器支持for ... of');
for ... of
循环和for ... in
循环有何区别?
for ... in
循环遍历的实际上是对象的属性名称,当给Array
对象添加了额外的属性后,遍历时会遍历得到
for ... of
,只循环集合本身的元素,添加的额外属性并不会遍历得到
函数定义
第一种:
function abs(x) {
if (x >= 0) {
return x;
} else {
return -x;
}
}
第二种:(匿名方式)
var abs = function (x) {
if (x >= 0) {
return x;
} else {
return -x;
}
};
arguments关键字
arguments
,它只在函数内部起作用,利用arguments
,你可以获得调用者传入的所有参数。arguments
类似Array
但它不是一个Array
:
// foo(a[, b], c)
// 接收2~3个参数,b是可选参数,如果只传2个参数,b默认为null:
function foo(x) {
console.log('x = ' + x); // 10
for (var i=0; i<arguments.length; i++) {
console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
}
}
foo(10, 20, 30);
rest关键字
rest
参数只能写在最后,前面用...
标识,传入的参数先绑定a
、b
,多余的参数以数组形式交给变量rest
如果传入的参数连正常定义的参数都没填满,也不要紧,rest
参数会接收一个空数组(注意不是undefined
)。
function foo(a, b, ...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);
}
foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]
变量的局部作用域
function foo() {
for (var i=0; i<100; i++) {
//
}
i += 100; // 仍然可以引用变量i
}
js中用let
替代var
可以申明一个块级作用域的变量
function foo() {
var sum = 0;
for (let i=0; i<100; i++) {
sum += i;
}
// SyntaxError:
i += 1;
}
解构赋值(直接对多个变量同时赋值)
数组元素进行解构赋值时,用[...]
括起来
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
// x, y, z分别被赋值为数组对应元素:
console.log('x = ' + x + ', y = ' + y + ', z = ' + z);
对对象进行解析赋值时,用{...}
括起来
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678',
school: 'No.4 middle school'
};
// 把passport属性赋值给变量id:
let {name, passport:id} = person;
name; // '小明'
id; // 'G-12345678'
// 注意: passport不是变量,而是为了让变量id获得passport属性:
passport; // Uncaught ReferenceError: passport is not defined
解构赋值在很多时候可以大大简化代码,例如,交换两个变量x
和y
的值
var x=1, y=2;
[x, y] = [y, x]
方法
在一个对象中绑定函数,称为这个对象的方法。
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了
在一个方法内部,this
是一个特殊变量,它始终指向当前对象
在一个函数内部如果调用了this
,那么这个this
指向视情况而定
举例:
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25, 正常结果
getAge(); // NaN
如果以对象的方法形式调用,比如xiaoming.age()
,该函数的this
指向被调用的对象,要保证this
指向正确,必须用obj.xxx()
的形式调用
如果单独调用函数,比如getAge()
,此时,该函数的this
指向全局对象,也就是window
箭头函数
=> x * x
相当于匿名函数:
function (x) {
return x * x;
}
标准对象
//包装类
var n = new Number(123); // 123,生成了新的包装类型
//普通函数
var n = Number('123'); // 123,相当于parseInt()或parseFloat()
构建对象
调用构造函数千万不要忘记写new
。为了区分普通函数和构造函数,按照约定,构造函数首字母应当大写,而普通函数首字母应当小写
function Student(name) {
this.name = name;
this.hello = function () {
alert('Hello, ' + this.name + '!');
}
}
var xiaoming = new Student('小明');
xiaoming.name; // '小明'
xiaoming.hello(); // Hello, 小明!
回调函数的同步和异步
同步优先、异步靠边、回调垫底
同步回调函数(函数的执行顺序从上而下)
var callback = function(arg3) {
console.log('callback Totle is:' + arg3)
}
function fn(arg1, arg2, cb) {
var Total = arg1 + arg2;
cb(Total);
console.log('mainFunction Totle is:' + Total)
}
fn(2, 2, callback) // 调用fn()函数,并传入2, 2, callback作为参数
异步回调函数(在函数的最后执行)
如何判断?
setTimeout()函数支持异步处理
function f2() {
console.log('f2 finished')
}
function f1(cb) {
setTimeout(cb,1000) //用setTimeout()模拟耗时操作
console.log('f1 finished')
}
f1(f2); //得到的结果是 f1 finished ,f2 finished