本文主要记录平时开发遇到的知识点和小技巧
相等判断(==)
类型相同: 判断其值是否相同
类型不同:
1. 如果数字和字符串比较, 则字符串会被隐式转换为数字,在做判断。
2. 如果有一方是布尔值, 则true 转换为 1, false 转换为 0 ,再进行判断。
3. 如果其中有一个值为对象, 则对象会调取自身的valueOf 或者toString方法进行转换,再做判断。
4.undefined 与 null 相等。
等同运算符(===)
类型不同: 返回false
类型相同:
1. 如果同为数字/字符串, 则比较值
2. 如果同为布尔值, 相同则为true, 不同为false
3. 如果两个操作数同为引用类型,且引用的为同一个对象(函数,数组),则相同。
所以使用 === 进行逻辑判断的时候,自己就要很清楚两边的数据类型。 比如调用函数得到的是字符串'1', 与 数字 1 比较的时候,得到的false, 不要犯类似的低级错误。
this指针
javascript中, this表示当前上下文, 即调用者的引用。
var tom = {
sex: 'M',
age: 20
}
var jerry = {
sex: 'F',
age: 18
}
function getAge() {
return this.age;
}
console.log(getAge.call(tom)); // 20
console.log(getAge.call(jerry)); // 18
// 通过call 方法, 改变了getAge函数中this的指向, this不会指向getAge函数本身。
var person = {
first: 'john',
last: 'tom',
getFull: function () {
console.log(this.first + ' ' + this.last);
}
}
person.getFull(); // john tom
//this 指向person
var firstName = 'will';
var lastName = 'smith';
function getFull() {
console.log(this.firstName + ' ' + this.lastName);
}
getFull(); // will smith
//调用者是window,所以 this 指向window。
function a() {
console.log(this);
}
a.call(null);
call 调用一个对象的一个方法,以另一个对象替换当前对象。
格式如 call(thisObj, arg1,arg2...argN);
在函数体外部调用call()方法,如果传入null,则默认转成window,如果不传也是一样,即函数中的this指向window。
console.log(this) // window;
function a() {
console.log(this === window);
}
console.log(this === window); // true
a.call(); // true
a.call(null); // true
a.call(this); // true
a.call(window); // true
a(); // true
this 的值并非取决于如何被定义, 而是取决于
调用方式
。
更多有关函数调用的内容请翻阅javascript语言精粹第四章的函数部分。
更多有关this的内容可以翻阅《你不知道的javascript上卷》第二章。
变量声明与函数声明提升
JavaScript会将所有变量和函数声明移动到它的作用域的最前面,这就是所谓的变量提升(Hoisting)。
也就是说,无论你在什么地方声明变量和函数,解释器都会将它们移动到作用域的最前面。因此我们可以先使用变量和函数,而后声明它们.
但是,仅仅是变量声明被提升了,而变量赋值不会被提升。
如果你不明白这一点,有时则会出错:
console.log(a); // 输出undefined
a = 2; // 初始化y
// 上面的代码等同于
var a; // 声明y
console.log(a); // 输出undefined
a = 2; // 初始化y
再看一个:
var a;
console.log(a);
a = 1;
function a() {
// xxx
}
输出:
/*
function a() {
// xxx
}
1
*/
javascript永远是先解析声明函数,再解析变量。
执行顺序如下:
(1) 解析函数a;
(2) 声明变量var a; 因为a此时并没有被赋值,所以它为 undefined, 还是指向原来的值,即函数 function a;
(3) console.log(a); // function a
(4) a = 1; // 重新赋值, 输出1
函数重载
javascript中是没有函数重载的,但是javascript的函数没有限制传入的参数个数必须与函数接收参数的个数相同,所以我们可以利用这一特性来模拟函数重载。
举个栗子:
function add() {
if (arguments.length < 2) {
return arguments[0];
} else {
var _args = [].slice.call(arguments);
return _args.reduce(function (a, b) {
return a + b;
})
}
}
add(1); // 1
add(1, 2, 3, 4); // 10
举个计算日期的栗子:
// ..
getFutureDate: function(startDate, afterYear, afterMonth, afterDay) {
var futureDate, year, month, day;
if (arguments.length === 3) {
afterDay = arguments[2];
afterMonth = arguments[1];
afterYear = arguments[0];
startDate = new Date(startDate);
}
if (arguments.length === 4 && Object.prototype.toString.call(startDate) !== "[object Date]") {
startDate = new Date(startDate);
getFutureDate: function (startDate, afterYear, afterMonth, afterDay) {
var futureDate, year, month, day;
if (arguments.length === 3) {
afterDay = arguments[2];
afterMonth = arguments[1];
afterYear = arguments[0];
startDate = new Date(startDate);
}
if (arguments.length === 4 && Object.prototype.toString.call(startDate) !== "[object Date]") {
startDate = new Date(startDate);
}
//计算年
futureDate = startDate.setFullYear(startDate.getFullYear() + parseInt(afterYear));
futureDate = new Date(futureDate);
// 计算月
futureDate = futureDate.setMonth(futureDate.getMonth() + parseInt(afterMonth));
futureDate = new Date(futureDate);
// 计算日
futureDate = futureDate.setDate(futureDate.getDate() + parseInt(afterDay));
futureDate = (new Date(futureDate));
year = futureDate.getFullYear();
month = futureDate.getMonth() + 1;
month = month < 10 ? '0' + month : month;
day = futureDate.getDate();
day = day < 10 ? '0' + day : day;
futureDate = [year, month, day].join('-');
return futureDate
},
initDateTime: function () {
// ...
var endTime = _that.getFutureDate(new Date(today.replace(/-/g, "/")).getTime(), 0, maxInsuranceMonth, maxInsuranceDay);
// ...
}
// ...
Map 函数
var ary = [1, 2, 3, 4, 5];
var res = ary.map(function (item, index, input) {
return item * 10;
});
console.log(res); // [10, 20, 30, 40, 50]
console.log(ary); // [1, 2, 3, 4, 5]
map 函数的实现:
Array.prototype.map = function (func /*, obj */) {
var len = this.length;
//check the argument
if (typeof func != "function") {
throw new Error("argument should be a function!");
}
var res = [];
var obj = arguments[1];
for (var i = 0; i < len; i++) {
//func.call(), apply the func to this[i]
res[i] = func.call(obj, this[i], i, this);
}
return res;
}
map:和forEach非常相似,都是用来遍历数组中的每一项值的,用来遍历数组中的每一项;
区别:map的回调函数中支持return返回值;return的是啥,相当于把数组中的这一项变为啥(并不影响原来的数组,只是相当于把原数组克隆一份,把克隆的这一份的数组中的对应项改变了);
前面已经说过,this会指向调用者,所以this是指向需要用到map函数的数组的。
需要注意的是,map函数是接收2个参数的,第二个参数是第一个参数的函数this指向。
柯里化
柯里化就是预先将函数的某些参数传入,得到一个简单的函数,但是预先传入的参数被保存在闭包中,因此会有一些奇特的特性。
var adder = function(num) {
return function(y) {
return num + y;
}
}
console.log(adder(1)(100)); // 101
console.log(adder(2)(100)); // 102
更多内容请翻阅上一篇介绍《邂逅函数柯里化》
递归
递归在编程中会经常使用,在某些时候,递归可以给我们减少很多代码冗余。
比如我们的求阶乘函数:
function factorial(n) {
if (n == 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}
函数不停的调用自身,来达到不停的向下求值相乘,从而实现阶乘求值。代码逻辑也一目了然。
客户端判断
var UA = (function (userAgent) {
var ISOldIOS = /OS (\d)_.* like Mac OS X/g.exec(userAgent),
isOldAndroid = /Android (\d.*?);/g.exec(userAgent) || /Android\/(\d.*?) /g.exec(userAgent);
// 判断设备是否是IOS7以下
// 判断设备是否是android4.5以下
// 判断是否iOS
// 判断是否android
// 判断是否QQ浏览器
return {
oldIOS : ISOldIOS ? +ISOldIOS.pop() < 8 : false,
oldAndroid: isOldAndroid ? +isOldAndroid.pop().substr(0, 3) < 4.5 : false,
iOS : /\(i[^;]+;( U;)? CPU.+Mac OS X/.test(userAgent),
android : /Android/g.test(userAgent),
mQQBrowser: /MQQBrowser/g.test(userAgent)
}
})(navigator.userAgent);