1、方法
1.1 方法的定义
方法不就是函数?还是有点区别的,在一个对象中绑定函数,称为这个对象的方法。var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
xiaoming.age; // function xiaoming.age() 返回函数内容
xiaoming.age(); // 2017年调用是27,第二年调用就变成28了11
1
var xiaoming = { 2
name: '小明',3
birth: 1990,4
age: function () {5
var y = new Date().getFullYear();6
return y - this.birth;7
}8
};9
10
xiaoming.age; // function xiaoming.age() 返回函数内容11
xiaoming.age(); // 2017年调用是27,第二年调用就变成28了1.2 this关键字
上面的函数中用到了一个特殊的关键字,this,在一个方法内部,它始终指向当前对象,也就是xiaoming这个变量,所以this.birth可以拿到xiaoming的birth属性。
然而这个this也可以说是JavaScript中设计的一个缺陷,为什么呢?结合以上,我们看下面的代码:
var fn = xiaoming.age; // 先拿到xiaoming的age函数
fn(); // NaN2
1
var fn = xiaoming.age; // 先拿到xiaoming的age函数2
fn(); // NaN是不是觉得很奇怪,为什么会得到NaN?this在之前不是指向了xiaoming吗?
实际上,this的指向是随情况而定的,如果我们使用 xiaoming.age()的方式,this才是正确指向xiaoming(如果普通函数没有写在对象里,实际上this又会指向window对象,即全局对象),所以,要保证this的指向正确,必须用obj.xxx()的形式调用方法!
诸如以下的方式也是不行的,this的指向只在age的方法中指向xiaoming,方法中嵌套是无效的,会重新指向全局对象window:
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - this.birth;
}
return getAgeFromBirth();
}
};
xiaoming.age();13
1
var xiaoming = {2
name: '小明',3
birth: 1990,4
age: function () {5
function getAgeFromBirth() {6
var y = new Date().getFullYear();7
return y - this.birth;8
}9
return getAgeFromBirth();10
}11
};12
13
xiaoming.age();1.3 缺陷修复
1.3.1 申请额外变量获取
想要修复如此问题,只能再申请一个变量来捕获this:
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var that = this; // 在方法内部一开始就捕获this
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - that.birth; // 用that而不是this
}
return getAgeFromBirth();
}
};
xiaoming.age();14
1
var xiaoming = {2
name: '小明',3
birth: 1990,4
age: function () {5
var that = this; // 在方法内部一开始就捕获this6
function getAgeFromBirth() {7
var y = new Date().getFullYear();8
return y - that.birth; // 用that而不是this9
}10
return getAgeFromBirth();11
}12
};13
14
xiaoming.age();1.3.2 apply和call方法
或者使用函数本身的 apply 方法,来控制this的指向。
该方法有两个参数,第一个参数是需要绑定的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, 参数为空13
1
function getAge() {2
var y = new Date().getFullYear();3
return y - this.birth;4
}5
6
var xiaoming = {7
name: '小明',8
birth: 1990,9
age: getAge10
};11
12
xiaoming.age(); // 2513
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空你可以这样理解,就类似于Java中类的非静态方法,它是属于对象的,不能直接使用,所以apply这里要求你定义两个东西,一个是对象,一个是必要参数。
还有一个和apply()类似的方法是 call() ,不同的是,call() 的参数传入直接按顺序传入,不需要打包成Array:
Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 52
1
Math.max.apply(null, [3, 5, 4]); // 52
Math.max.call(null, 3, 5, 4); // 5思维拓展:用apply动态改变函数的行为
假定我们想统计一下代码一共调用了多少次parseInt(),可以把所有的调用都找出来,然后手动加上count += 1,不过这样做太傻了。
最佳方案是用我们自己的函数替换掉默认的parseInt():
var count = 0;
var oldParseInt = parseInt; // 保存原函数
window.parseInt = function () {
count += 1;
return oldParseInt.apply(null, arguments); // 调用原函数
};
// 测试:
parseInt('10');
parseInt('20');
parseInt('30');
count; // 313
1
var count = 0;2
var oldParseInt = parseInt; // 保存原函数3
4
window.parseInt = function () {5
count += 1;6
return oldParseInt.apply(null, arguments); // 调用原函数7
};8
9
// 测试:10
parseInt('10');11
parseInt('20');12
parseInt('30');13
count; // 32、高阶函数
我们知道函数实际也是一个对象,也可以i用变量指向函数,既然如此,那么我们完全可以将一个指向函数的变量a,作为另外一个函数的接收参数,这就是高阶函数:
function add(x, y, f) {
return f(x) + f(y);
}
当我们调用add(-5, 6, Math.abs)时,参数x,y和f分别接收-5,6和函数Math.abs:
x = -5;
y = 6;
f = Math.abs;
f(x) + f(y) ==> Math.abs(-5) + Math.abs(6) ==> 11;
return 11;10
1
function add(x, y, f) {2
return f(x) + f(y);3
}4
5
当我们调用add(-5, 6, Math.abs)时,参数x,y和f分别接收-5,6和函数Math.abs:6
x = -5;7
y = 6;8
f = Math.abs;9
f(x) + f(y) ==> Math.abs(-5) + Math.abs(6) ==> 11;10
return 11;2.1 map
map() --> 定义在JavaScript中的数组Array中,在map()中传入自己的函数,可以得到一个新的Array作为结果:
function pow(x) {
return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]6
1
function pow(x) {2
return x * x;3
}4
5
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];6
arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]map作为高阶函数,实际上把运算规则抽象了,上例中,实际上你也可以分别遍历出元素,然后每次把元素进行函数运算后的结果push到一个新的Array里,最终把这个Array返回。
所以map更能直观地表述:把f(x)作用在Array的每一个元素并把结果生成一个新的Array
2.2 reduce
reduce() --> 把函数作用在Array的[x1, x2, x3...]上,这个参数函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算:
e.g.
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)2
1
e.g.2
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)比如对一个Array求和,如果用reduce实现的话就是:
var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
return x + y;
}); // 25
//实际上就是1和3作为参数,带入函数运算得到4
//作为结果的4再和下一个元素5带入函数继续运算得到结果9
//循环,即最终得到258
1
var arr = [1, 3, 5, 7, 9];2
arr.reduce(function (x, y) {3
return x + y;4
}); // 255
6
//实际上就是1和3作为参数,带入函数运算得到47
//作为结果的4再和下一个元素5带入函数继续运算得到结果98
//循环,即最终得到25继续拓展一下思维,把[1, 3, 5, 7, 9]变换成整数13579:
var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
return x * 10 + y;
}); // 135794
1
var arr = [1, 3, 5, 7, 9];2
arr.reduce(function (x, y) {3
return x * 10 + y;4
}); // 135792.3 filter
filter() --> 把传入的函数依次作用于Array的每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素
//比如去掉一个Array中的偶数
var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function (x) {
return x % 2 !== 0;
});
r; // [1, 5, 9, 15]6
1
//比如去掉一个Array中的偶数2
var arr = [1, 2, 4, 5, 6, 9, 10, 15];3
var r = arr.filter(function (x) {4
return x % 2 !== 0;5
});6
r; // [1, 5, 9, 15]为了去掉空字符串,利用filter和JS中对于boolean的判断:
var arr = ['A', '', 'B', null, undefined, 'C', ' '];
var r = arr.filter(function (s) {
return s && s.trim(); // 注意:IE9以下的版本没有trim()方法
});
r; // ['A', 'B', 'C']5
1
var arr = ['A', '', 'B', null, undefined, 'C', ' '];2
var r = arr.filter(function (s) {3
return s && s.trim(); // 注意:IE9以下的版本没有trim()方法4
});5
r; // ['A', 'B', 'C']filter()接收的回调函数,其实可以有多个参数。通常我们仅使用第一个参数,表示Array的某个元素。回调函数还可以接收另外两个参数,表示元素的位置和数组本身:
var arr = ['A', 'B', 'C'];
var r = arr.filter(function (element, index, self) {
console.log(element); // 依次打印'A', 'B', 'C'
console.log(index); // 依次打印0, 1, 2
console.log(self); // self就是变量arr
return true;
});7
1
var arr = ['A', 'B', 'C'];2
var r = arr.filter(function (element, index, self) {3
console.log(element); // 依次打印'A', 'B', 'C'4
console.log(index); // 依次打印0, 1, 25
console.log(self); // self就是变量arr6
return true;7
});拓展思维:利用filter巧妙去重:
var
r,
arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];
r = arr.filter(function (element, index, self) {
return self.indexOf(element) === index;
});
alert(r.toString());x
1
var2
r,3
arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];4
5
r = arr.filter(function (element, index, self) {6
return self.indexOf(element) === index;7
});8
9
alert(r.toString());2.4 sort
sort作为排序的算法,想必其使用频率不言而喻,但是有一个大坑,那就是Array的sort()方法,默认是把所有元素转换为String再排序,这就会导致普通的数字排序中,例如10会排列在2的前面,不是因为10比2小,而是因为'10'中的字符'1'比‘2'的ASCII码小。另外,要注意的是,sort()方法是直接对原Array直接做出修改,返回当前的Array,如果你希望只是得到修改后的Array而不改变源Array,记得另起变量保存以后再执行sort。
还好的是,sort()也是一个高阶函数,可以接收一个比较函数来实现自定义排序,类似Java中集合实现排序,元素要覆盖Comparable接口的CompareTo方法一样,所以在sort()中如果要实现按数字大小排序,可以这样写:
var arr = [10, 20, 1, 2];
arr.sort(function (x, y) {
if (x < y) {
return -1;
}
if (x > y) {
return 1;
}
return 0;
}); // [1, 2, 10, 20]10
1
var arr = [10, 20, 1, 2];2
arr.sort(function (x, y) {3
if (x < y) {4
return -1;5
}6
if (x > y) {7
return 1;8
}9
return 0;10
}); // [1, 2, 10, 20]还有诸如对字符串进行忽略大小写的字母排序:
var arr = ['Google', 'apple', 'Microsoft'];
arr.sort(function (s1, s2) {
x1 = s1.toUpperCase();
x2 = s2.toUpperCase();
if (x1 < x2) {
return -1;
}
if (x1 > x2) {
return 1;
}
return 0;
}); // ['apple', 'Google', 'Microsoft']1
var arr = ['Google', 'apple', 'Microsoft'];2
arr.sort(function (s1, s2) {3
x1 = s1.toUpperCase();4
x2 = s2.toUpperCase();5
if (x1 < x2) {6
return -1;7
}8
if (x1 > x2) {9
return 1;10
}11
return 0;12
}); // ['apple', 'Google', 'Microsoft']