方法
什么是js的方法?简单讲,绑定到对象的函数就是方法。
this
在对象的方法中,我们常常使用this关键字。this关键字代表方法所绑定的对象。
var wangqiang = { name : "wangqiang", age : 18, city : "guangzhou", address : "tianhe", //绑定到对象的函数叫方法 getBirth:function(){ let now = new Date(); return now.getFullYear() - this.age; } } wangqiang.getBirth(); //2005 var birth = wangqiang.getBirth; birth(); //NaN,脱离了绑定的对象。
上述代码所示,在方法getBirth()内部,我们用到了一个this关键字。这个关键字指向了绑定的对象,要正确执行方法,需要按照 obj.xxx()的方式才能正确地调用方法,保证方法绑定的对象,this的指向才能正确。
再看如下代码:
var lili = { name : "lili", age : 12, city : "beijing", address : "chaoyang", getBirth : function(){ let diff = function(year){ //超出了this的作用范围,this为undefined return year-this.age; } let now = new Date(); console.log(diff(2022)); return now.getFullYear() - this.age; } }
在getBirth方法中,我们有定义了一个匿名函数。在匿名函数不能再使用this,因为这时的this是undefined。我们可以在匿名函数外部用一个 变量接收this,在匿名函数内部再使用这个变量。也就是this对象不能跨越两层函数进行使用。
var lili = { name : "lili", age : 12, city : "beijing", address : "chaoyang", //方法的简写 getBirth(){ let that = this; let diff = function(year){ return year-that.age; } let now = new Date(); console.log(diff(2022)); return now.getFullYear() - this.age; } }
为此,可以在第一次函数里用一个变量that接收this,在第二层函数里再使用that,从而避免this变量不能跨越两层的障碍。
apply
函数通过apply方法,应用到对象上。
var xiaoli = { name : "xiaoli", age : 12, city : "beijing", address : "chaoyang", birth : getBirth } function getBirth(){ let now = new Date(); return now.getFullYear() - this.age; } xiaoli.birth(); getBirth.apply(xiaoli,[]);//应用到xiaoli对象上,参数数组为[] getBirth(); // Uncaught TypeError
另一个与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
闭包
在JavaScript中,一个函数可以作为另外一个函数的参数进行传递。同样地,一个函数也可以作为另外一个函数的返回值。例如:
function lazy_sum(...rest){ return function sum(){ let reduce = rest.reduce(function(s,x){ return s += x; },6); return reduce; } } var lazy_sum1 = lazy_sum(1,2,4,6); var lazy_sum2 = lazy_sum(1,2,4,6); lazy_sum1(); //调用sum()函数,返回13 lazy_sum2(); //调用sum()函数,返回13 typeof lazy_sum1(); // 返回function typeof lazy_sum2(); // 返回function lazy_sum1 == lazy_sum2 // false
上述代码所示,调用lazy_sum1和lazy_sum2函数,它们都返回一个sum()函数。虽然返回的名称相同,但不同于数值的相等比较,它们并不相等。
看起来作用不大,只是可以延迟调用而已。再看下边的例子:
//闭包可以在函数内部封装一个变量 function wrap(initial){ initial = initial || 0; return function inc(){ initial += 1; return initial; } } var w = wrap(2); w(); //返回3 w(); //返回4 w(); //返回5
上述代码所示,wrap()是一个函数,它接收一个initial作为参数,这个函数的返回值也是一个函数,它是inc()。我们将inc()函数赋值给变量w, w函数调用一次返回3。接下来再调用一次,其内部的变量initial已经保留了3这个值,所以这时的返回值就是4。接下来的每次调用逻辑都是一致的。 这种在函数中封装了一个变量的函数形成了闭包。这种在函数中封装既封装了变量,又封装了方法的闭包,是不是有点像Java中的类呢?
闭包
ES6标准新增了一种新的函数:Arrow Function(箭头函数)。为什么叫Arrow Function?因为它的定义用的就是一个箭头:
x => x * x
使用规则
上面的箭头函数相相当于一个匿名函数:
function (x) { return x * x; }
箭头函数简化了函数定义。箭头函数有两种格式,一种像上面的,只包含一个表达式,连{ ... }和return都省略掉了。 还有一种可以包含多条语句,这时候就不能省略{ ... }和return:
x => { if (x>0){ return x; }else{ return -x; } }
如果函数有多个参数,需使用括号,参数之间以逗号隔开
// 两个参数: (x, y) => x * x + y * y // 无参数: () => 3.14 // 可变参数: (x, y, ...rest) => { var i, sum = x + y; for (i=0; i < rest.length; i++) { sum += rest[i]; } return sum; }
如果要返回一个对象,就要注意,如果是单表达式,这么写的话会报错:
// SyntaxError: x => { foo: x }
表达式的对象和函数体的{...}相冲突,需在对象外加上括号:
x => ({foo:x});
前面章节指出了对象方法上,this使用的注意事项:
var lili = { name : "lili", age : 12, city : "beijing", address : "chaoyang", getBirth(){ let diff = function(year){ return year-this.age; // this指向window或undefined }; let now = new Date(); return now.getFullYear() - this.age; } }
现在,箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj:
var lili = { name : "lili", age : 12, city : "beijing", address : "chaoyang", getBirth(){ let diff = (year)=>year-this.age; // this指向lili let now = new Date(); return now.getFullYear() - this.age; } }
文章同时发表在:码农编程网 欢迎访问
本节重点:
- 什么是方法,方法的调用;
- 注意对象方法内部的this指向,避免指向错误;
- apply,call的使用和区别;
- 定义一个返回类型为函数的函数;
- 如何形成闭包,闭包的使用;
- 什么是箭头函数,箭头函数的使用;
- 箭头函数修复了对象方法中的this指向。