想像一下,今天你是负责帮忙点菜的服务生,来了一桌客人,拿着菜单跟你说 「 我要 this this this ! 」,请问你知道他点三小什麽吗,相信除非你会通灵不然光听绝对猜不出来,那怎麽判断呢?
看客人的手指向哪道菜判断那个 this 对应谁
在 JavaScript 中也会遇到这种不直接告诉你 this 是谁的情况,虽然这里没有手指头做参考,但别怕,教你另一招绝对精准的判断方式,包你以後一秒指认 this !
怎麽看出 this 是谁? 从呼叫方式判断
不同的呼叫方法下的 this 会不相同,呼叫方式可分为以下四种:
接下来就一一从每个呼叫方法来认识 this 是谁吧!
最直接的函式呼叫方式,就是在函式名称後加上一个 ()
不论是 function declaration 或 function expression 或 立即函式 都算在一般的函式呼叫内。
这时的 this 是谁:在一般函式呼叫下的 this 代表的是全域,在严格模式下则是 undefined。
//函式宣告
function func(){
console.log(this)
}
func() // window
//函式表达式
let func = function(){
console.log(this)
}
func() // window
//立即函式
(function (){ console.log (this)})()
当函式成为一个物件内的属性时,就可以称该函式为一个 方法method,呼叫的语法像是 物件名称.函式名称()
这时的 this 是谁:作为 method 呼叫下的 this 会指向 method 所属的物件。
var dish = '黄金开口笑'
function order() {
console.log (this.dish)
}
let customer1 = {
dish: '烈冰鲜鲷山',
orderMore: order
}
let customer2 = {
dish: '奇蹟彗星炒饭',
orderMore: order
}
// 以下请自行加上 console.log
customer1.orderMore() // '烈冰鲜鲷山' ,this 指向 customer1
customer2.orderMore() // '奇蹟彗星炒饭',this 指向 customer2
order() // '黄金开口笑', 一般函式呼叫 this 指向全域
变化题,若是将 customer1.orderMore
这个 method 存入一个变数中再进行呼叫呢?
let secondDish = customer1.orderMore; // 将 customer1 的 orderMore 存入另一个变数再呼叫
secondDish() // 黄金开口笑,此时的呼叫属於一般函式呼叫,this 指向全域
// 除非是将使用 method 呼叫後的结果存入变数,该变数才会是 this 指向物件的结果
function order() {
return (this.dish)
}
let secondDish = customer1.orderMore();
console.log( secondDish ) // 烈冰鲜鲷山
上一篇讲到使用关键字 new
和建构式 constructor 创物件,这时的 this 会指向这个新创的物件,详见 D13 - 做出鸡蛋糕 new + Constructor
这时的 this 是谁:使用 new + constructor 下的 this 是新创的那个物件。
function Order(dish) {
this.dish = dish
}
let customer1 = new Order('烈冰鲜鲷山')
let cusomter2 = new Order('奇蹟彗星炒饭')
console.log( customer1 ) // Order {dish: "烈冰鲜鲷山"}
console.log( customer2 ) // Order {dish: "奇蹟彗星炒饭"}
点菜时除了依照客户的喜好,服务生也可以推荐菜色,让客人更改 「this」的选项为餐厅主打的招牌菜。
JavaScript 内也可以指定我们要的 this,透过所有函式都具备的两种方法:apply 和 call,在呼叫函式的同时,在参数内放入希望 this 指向的物件。
这时的 this 是谁:使用 call 和 apply 小括号内放的第一个引数。
var dish = '黄金开口笑'
function order() {
console.log (this.dish)
}
let customer1 = {
dish: '烈冰鲜鲷山',
orderMore: order
}
let customer2 = {
dish: '奇蹟彗星炒饭',
orderMore: order
}
customer1.orderMore.call(customer2) // 奇蹟彗星炒饭,强制 this 指向 customer2
customer2.orderMore.apply(window) // 黄金开口笑,强制 this 指向全域 window
order() // 黄金开口笑, 一般函式呼叫 order, this 指向全域
order.call(customer1) // 烈冰鲜鲷山, 透过 call 绑定 this 指向 customer1
两者唯一的差异是引数的格式,第一个都是放 this 指定的物件,剩下的放欲带入函式的引数
function.call ( 指定 this 的物件, 引数1, 引数2, 引数3)
function.apply ( 指定 this 的物件, [引数1, 引数2, 引数3])
call 可以接受任一数量的引数,适用在有多个彼此不相关的变数或实质
apply 的引数须包在阵列内
除了上面 call 和 apply 可以指定 this,另外两种绑定 this 的方式为: bind 和 箭头函式
为什麽要分开写呢? 因为这两种并不会进行函数呼叫,无法从呼叫判定,两者的 this 都是在定义时就绑定好
bind 的 this 绑定跟 apply 和 call 一样,在 ()
中放入 指定的 this 物件
这时的 this 是谁:bind 依照小括号内放置的物件
var dish = '黄金开口笑'
function order() {
console.log (this.dish)
}
let customer1 = {
dish: '烈冰鲜鲷山',
orderMore: order// 绑定 this 为 customer 2
}
let customer2 = {
dish: '奇蹟彗星炒饭',
orderMore: order.bind(customer1)
}
customer2.orderMore() // 烈冰鲜鲷山
customer2.orderMore.call(customer2) // 烈冰鲜鲷山,无法透过 call, apply 重新指定 this
let specialMenu = order.bind({dish: 'JS吃到饱'}) // 绑定 order function 内的 this
specialMenu() // JS吃到饱
箭头函式为 ES6 加入的 function expression 新写法
箭头函式下的 this 会指向创建时的物件,一般几乎指向全域,除非是使用建构式生成时,箭头函式的 this 指向新创的物件,因此在箭头函式内使用 this 要特别留意,一但定义了就无法重新绑定罗!
这时的 this 是谁:箭头函式下的 this 可能指向全域或是创建的物件。
var dish = '黄金开口笑'
let customer1 = {
dish: '烈冰鲜鲷山',
orderMore: ()=> this.dish,
orderMoreMore: function(){
return (this.dish)
}
}
let customer2 = {
dish: '奇蹟彗星炒饭',
}
// 虽然使用 method 方式呼叫,但因 orderMore 使用箭头函式, this 已绑定在全域
customer1.orderMore() // 黄金开口笑
customer1.orderMore.call(customer2) // 黄金开口笑,绑定在全域下无法透过 call、apply 更改 this
// 一般匿名函式的 this 不会先被绑定,随呼叫不同而改变
customer1.orderMoreMore() // 烈冰鲜鲷山
customer1.orderMoreMore.call(customer2) // 奇蹟彗星炒饭
一般情况下的 this 都会指向全域,但在 constructor 内使用箭头函式的 this 可以绑定在新物件上
function Order(dish) {
this.dish = dish;
this.orderMore = ()=> this.dish
}
let customer1 = new Order('烈冰鲜鲷山')
let customer2 = new Order('奇蹟彗星炒饭')
customer1.orderMore() // 烈冰鲜鲷山,箭头函式内的 this 指向新建立的物件 customer1
customer2.orderMore() // 奇蹟彗星炒饭,箭头函式内的 this 指向新建立的物件 customer2
以後遇到 this 想知道它到底指向谁,记住先看它是怎麽被呼叫的,再次整理如下
[008重新认识 JavaScipt]
忍者 JavaScript 开发技巧探秘第二版 by John Resig, Bear Bibeault, Josip Maras
<<: 【Day 16】深度学习(Deep Learning)--- Tip(一)
>>: Day 15 -资料查询语言 INNER JOIN!
虽然之前有看过 slice / array 比较的文章, 但在写 leetcode 时还是碰到点小麻...
大家好~ 我是五岁~ 今天来画四足战车的草图细节~ 首先来画上半部 依照昨天的外型草稿,进一步认真的...
这篇文章会介绍如何使用DOM来处理表单的物件存取,以及利用条件判断式来处理表单的验证,像是在上一篇的...
人的科技文明发展始终来自於人性 在现今的科技与资讯发达的社会,人手一机已不再是奢望,连小小年纪的小朋...
本篇文章同步发布於个人部落格 (後续更新皆会以部落格为主): 什麽是 Github? 本系列文章会以...