JavaScript允许对任意数据类型做比较
false == 0; // true false === 0; // false
不要使用==比较,始终坚持使用===比较
JavaScript的设计者希望用null表示一个空的值,而undefined表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用null。undefined仅仅在判断函数参数是否传递的情况下有用。
JavaScript的数组可以包括任意数据类型
[1, 2, 3.14, 'Hello', null, true];
JavaScript对象的键都是字符串类型,值可以是任意数据类型
使用var申明的变量则不是全局变量,它的范围被限制在该变量被申明的函数体内
在strict模式下运行的JavaScript代码,强制通过var申明变量,未使用var申明变量就使用的,将导致运行错误。
不用var申明的变量会被视为全局变量,为了避免这一缺陷,所有的JavaScript代码都应该使用strict模式。
var name = '小明';
var age = 20;
var message = `你好, ${name}, 你今年${age}岁了!`;
字符串是不可变的,如果对字符串的某个索引赋值,不会有任何错误,但是,也没有任何效果:
var s = 'Test'; s[0] = 'X'; alert(s); // s仍然为'Test'
直接给Array的length赋一个新的值会导致Array大小的变化
var arr = [1, 2, 3]; arr.length; // 3 arr.length = 6; arr; // arr变为[1, 2, 3, undefined, undefined, undefined] arr.length = 2; arr; // arr变为[1, 2]
var arr = ['A', 'B', 'C'];
arr[1] = 99;
arr; // arr现在变为['A', 99, 'C']
slice()就是对应String的substring()版本,它截取Array的部分元素,然后返回一个新的Array
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
arr.slice(0, 3); // 从索引0开始,到索引3结束,但不包括索引3: ['A', 'B', 'C']
arr.slice(3); // 从索引3开始到结束: ['D', 'E', 'F', 'G']
复制一个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
** push()向Array的末尾添加若干元素,po p()则把Array的最后一个元素删除掉**
如果要往Array的头部添加若干元素,使用unshift()方法,shift()方法则把Array的第一个元素删掉
数组相关API
sort() 照默认顺序排序
splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:
var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
// 从索引2开始删除3个元素,然后再添加两个元素:
arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
// 只删除,不添加:
arr.splice(2, 2); // ['Google', 'Facebook']
arr; // ['Microsoft', 'Apple', 'Oracle']
// 只添加,不删除:
arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
- concat concat()方法把当前的Array和另一个Array连接起来,并返回一个新的Array
var arr = ['A', 'B', 'C'];
var added = arr.concat([1, 2, 3]);
added; // ['A', 'B', 'C', 1, 2, 3]
arr; // ['A', 'B', 'C']
- join 把当前Array的每个元素都用指定的字符串连接起来,然后返回连接后的字符串
var arr = ['A', 'B', 'C', 1, 2, 3];
arr.join('-'); // 'A-B-C-1-2-3'
xiaohong的属性名middle-school不是一个有效的变量,就需要用''括起来。访问这个属性也无法使用.操作符,必须用['xxx']来访问:
var xiaohong = {
name: '小红',
'middle-school': 'No.1 Middle School'
};
xiaohong['middle-school']; // 'No.1 Middle School'
如果我们要检测xiaoming是否拥有某一属性,可以用in操作符
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
不过要小心,如果in判断一个属性存在,这个属性不一定是xiaoming的,它可能是xiaoming继承得到的:
'toString' in xiaoming; // true
要判断一个属性是否是xiaoming自身拥有的,而不是继承得到的,可以用hasOwnProperty()方法:
var xiaoming = {
name: '小明'
};
xiaoming.hasOwnProperty('name'); // true
xiaoming.hasOwnProperty('toString'); // false
for循环的一个变体是for ... in循环,它可以把一个对象的所有属性依次循环出来:
var o = {
name: 'Jack',
age: 20,
city: 'Beijing'
};
for (var key in o) {
console.log(key); // 'name', 'age', 'city'
}
var o = {
name: 'Jack',
age: 20,
city: 'Beijing'
};
for (var key in o) {
if (o.hasOwnProperty(key)) {
console.log(key); // 'name', 'age', 'city'
}
}
var a = ['A', 'B', 'C'];
for (var i in a) {
console.log(i); // '0', '1', '2'
console.log(a[i]); // 'A', 'B', 'C'
}
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
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined
Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。
var s1 = new Set(); // 空Set var s2 = new Set([1, 2, 3]); // 含1, 2, 3
for...of
var a = ['A', 'B', 'C'];
for (var x in a) {
console.log(x); // '0', '1', '2'
}
for (var x of a) {
console.log(x); // 'A', 'B', 'C'
}
然而,更好的方式是直接使用iterable内置的forEach方法,它接收一个函数,每次迭代就自动回调该函数
a.forEach(function (element, index, array) { // element: 指向当前元素的值 // index: 指向当前索引 // array: 指向Array对象本身 console.log(element + ', index = ' + index); });
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) {
console.log(element);
});
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
console.log(value);
});
var a = ['A', 'B', 'C'];
a.forEach(function (element) {
console.log(element);
});
函数
由于JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多没有问题,传入的参数比定义的少也没有问题。
变长参数 ...rest
function foo() {
//return返回对象的写法
return {
name: 'foo'
};
}
减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中,把自己的代码全部放入唯一的名字空间MYAPP中,会大大减少全局变量冲突的可能。例如:
// 唯一的全局变量MYAPP:
var MYAPP = {};
// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函数:
MYAPP.foo = function () {
return 'foo';
};
用let替代var可以申明一个块级作用域的变量
function foo() {
var sum = 0;
for (let i=0; i<100; i++) {
sum += i;
}
// SyntaxError:
i += 1;
}
const来定义常量,const与let都具有块级作用域
解构赋值
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678',
school: 'No.4 middle school'
};
// name, age, passport分别被赋值为对应属性:
var {name, age, passport} = person;
name; // '小明'
age; //20
passport;// G-12345678
let {name, passport:id} = person;
name; // '小明'
id; // 'G-12345678'
passport;// Uncaught ReferenceError: passport is not defined
var x,y;
({x, y} = { name: '小明', x: 100, y: 200});
x;//100
y;//200
function buildDate({year, month, day, hour=0, minute=0, second=0}) {
return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);
}
buildDate({ year: 2017, month: 1, day: 1 });
// Sun Jan 01 2017 00:00:00 GMT+0800 (CST)
buildDate({ year: 2017, month: 1, day: 1, hour: 20, minute: 15 });
// Sun Jan 01 2017 20:15:00 GMT+0800 (CST)
apply方法可以控制this的指向,apply方法,它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身的参数
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, getAge函数参数为空
另一个与apply()类似的方法是call(),apply()把参数打包成Array再传入,call()把参数按顺序传入。
比如调用Math.max(3, 5, 4),分别用apply()和call()实现如下:
Math.max.apply(null, [3, 5, 4]); // 5 Math.max.call(null, 3, 5, 4); // 5
Array的reduce()把一个函数作用在这个Array的[x1, x2, x3...]上,这个函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算。
比方说对一个Array求和,就可以用reduce实现:
var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
return x + y;
}); // 25
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回
在JavaScript的世界里,一切都是对象,为了区分对象的类型,我们用typeof操作符获取对象的类型,它总是返回一个字符串:
typeof 123; // 'number' typeof NaN; // 'number' typeof 'str'; // 'string' typeof true; // 'boolean' typeof undefined; // 'undefined' typeof Math.abs; // 'function' typeof null; // 'object' typeof []; // 'object' typeof {}; // 'object'
包装对象 包装对象用new创建
var n = new Number(123); // 123,生成了新的包装类型 var b = new Boolean(true); // true,生成了新的包装类型 var s = new String('str'); // 'str',生成了新的包装类型
虽然包装对象看上去和原来的值一模一样,显示出来也是一模一样,但他们的类型已经变为object了!所以,包装对象和原始值用===比较会返回false:
typeof new Number(123); // 'object' new Number(123) === 123; // false
typeof new Boolean(true); // 'object' new Boolean(true) === true; // false
typeof new String('str'); // 'object' new String('str') === 'str'; // false
总结一下,有这么几条规则需要遵守:
不要使用new Number()、new Boolean()、new String()创建包装对象;
因为得到的是object对象
用parseInt()或parseFloat()来转换任意类型到number;
用String()来转换任意类型到string,或者直接调用某个对象的toString()方法;
123..toString(); // '123', 注意是两个点! (123).toString(); // '123'
通常不必把任意类型转换为boolean再判断,因为可以直接写if (myVar) {...};
typeof操作符可以判断出number、boolean、string、function和undefined;
判断Array要使用Array.isArray(arr);
判断null请使用myVar === null;
判断某个全局变量是否存在用typeof window.myVar === 'undefined';
函数内部判断某个变量是否存在用typeof myVar === 'undefined'。
如果要创建一个指定日期和时间的Date对象,可以用:
var d = new Date(2015, 5, 19, 20, 15, 30, 123);
我们还可以编写一个createStudent()函数,在内部封装所有的new操作。一个常用的编程模式像这样:
function Student(props) {
this.name = props.name || '匿名'; // 默认值为'匿名'
this.grade = props.grade || 1; // 默认值为1
}
Student.prototype.hello = function () {
alert('Hello, ' + this.name + '!');
};
function createStudent(props) {
return new Student(props || {})
}
这个createStudent()函数有几个巨大的优点:一是不需要new来调用,二是参数非常灵活,可以不传,也可以这么传:
var xiaoming = createStudent({
name: '小明'
});
xiaoming.grade; // 1
JavaScript由于采用原型继承,原型链走一走:
var arr = [1, 2, 3]; arr ----> Array.prototype ----> Object.prototype ----> null function foo() { return 0; } 函数也是一个对象,它的原型链是: foo ----> Function.prototype ----> Object.prototype ----> null 构造函数的原型链: xiaoming ----> Student.prototype ----> Object.prototype ----> null 用new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身 xiaoming.constructor === Student.prototype.constructor; // true Student.prototype.constructor === Student; // true Object.getPrototypeOf(xiaoming) === Student.prototype; // true xiaoming instanceof Student; // true
浏览器
window对象不但充当全局作用域,而且表示浏览器窗口。
navigator对象表示浏览器的信息。
screen对象表示屏幕的信息。
location对象表示当前页面的URL信息
document对象表示当前页面。由于HTML在浏览器中以DOM形式表示为树形结构,document对象就是整个DOM树的根节点。
AJAX请求是异步执行的,也就是说,要通过回调函数获得响应。
JQuery
简洁的操作DOM的方法:写$('#test')肯定比document.getElementById('test')来得简洁;
$本质上就是一个函数,但是函数也是对象,于是$除了可以直接调用外,也可以有很多其他属性
什么是jQuery对象?jQuery对象类似数组,它的每个元素都是一个引用了DOM节点的对象。
总之jQuery的选择器不会返回undefined或者null,这样的好处是你不必在下一行判断if (div === undefined)。
jQuery对象和DOM对象之间可以互相转化:
var div = $('#abc'); // jQuery对象 var divDom = div.get(0); // 假设存在div,获取第1个DOM元素 var another = $(divDom); // 重新把DOM包装为jQuery对象,这样就可以方便地使用jQuery的API了
按tag查找
var ps = $('p'); // 返回所有
节点
按class查找
var a = $('.red'); // 所有节点包含
class="red"都将返回 //通常很多节点有多个class,我们可以查找同时包含red和green的节点: var a = $('.red.green'); // 注意没有空格! // 符合条件的节点: //...//...
由于不同的浏览器绑定事件的代码都不太一样,所以用jQuery来写代码,就屏蔽了不同浏览器的差异,我们总是编写相同的代码。