JavaScript中的“this”全面解析

最近看你不知道的js书里头聊到this的时候说的很有意思,“任何足够先进的技术都和魔法无疑”。

以我的水平当然不能说this这个机制是不是足够先进,但是还是想试着分析分析他,因为它的坑实在太深了。

首先简明扼要说两点前提:1:this并不代表指向自身 2:this并不指向任何函数的词法作用域

对第二点稍微展开一下,因为词法作用域特性听起来虽然和对象类似,但是其实它是存在于JavaScript内部引擎中,无法通过代码访问。

 然后接下来是很重要的一句话:this并不是根据编写代码时的位置来绑定,它是在函数被调用的时候发生的绑定,它指向什么完全取决于函数是在哪里被调用。

寻找调用位置并不是简单分析代码这么简单,因为有的时候可能会有编程模式会隐藏调用。

我们重点是要分析调用栈,然后找到栈的第二个元素,这个就是真正的调用位置。

找到函数调用位置之后,我们就要说到js的四种this绑定规则:默认绑定,显式绑定,隐式绑定,new绑定。

默认绑定可以理解为当无法使用其他三种规则的使用规则(兜底),关于默认绑定主要要记住的就是,首先使用严格模式下,全局对象是不能用于默认绑定的,这个时候this会绑定到undefined,

且,严格模式下函数调用是可以的,不影响,你可以理解为严格模式这个意义不会“穿透”

function foo(){
  "use strict"
 console.log(this.a)

};
var a = 2;

foo()// this is undefined




function foo(){
  console.log(this.a)
}
var a = 2 
(function(){
  "use strict"
   foo() //2
}) ()

看到这里请注意理解哦再往下哦。

隐式绑定,我们说简单一点的话,就是看调用位置,有没有上下文对象,有的话就会倍绑定到这个对象上,且注意对象属性引用链只有上一层或者说最后一层在调用位置中起作用。

但是隐式绑定是很容易造成隐式丢失的,即丢失了绑定对象然后应用默认绑定。我们在这里比较精简的说法就是,在函数别名以及参数传递(隐式赋值)上都会体现隐式丢失,这个看后期有没有必要展开说。

到显式绑定,这个大家在日常开发或者学习中应该遇到过。即call,apply,bind方法,这些方法对js中绝大多数函数以及你自己创建的函数都是开放使用的。call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数。 就是说bind绑定后不会立即执行。

而call和apply的区别在于:参数的区别,call和aplly的第一个参数都是要改变上下文的对象,call()和apply()方法都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。apply和call方法的第一个参数都是特定的作用域第二个参数不同。

而call从第二个参数开始以参数列表的形式展现,需要逐个列出需要传递的参数。

apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数。可以是Array的实例,也可以是arguments对象。

最后一种绑定是new绑定,这里其实多说一句,js中的new操作符和别的面向类语言是不一样的,

严格上来说js中没有“构造函数”,只有对于函数的"构造调用",这个后期我也会再出一篇来专门讲js中的“类”.

四种绑定规则的优先级是:new>显>隐>默认。(这里建议自己多去试一试)

再说一个很有意思的事可以积累一下,有些时候我们会特意的去忽略绑定的this,比如在参数展开和函数柯里化的时候,我们甚至会指定this为null或者undefined,这样可以刻意帮我们回避默认绑定,当然,更安全的一种做法是使用一个DMZ对象,用Object.create(null)创建,即可保护全局对象.

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript创建对象的方式有以下几种: 1. 对象字面量方式 优点:简单方便,易于理解和维护。 缺点:无法实现继承和封装,对象的复用性较低。 示例代码: ```javascript var person = { name: 'Tom', age: 18, sayHello: function() { console.log('Hello, my name is ' + this.name + ', I am ' + this.age + ' years old.'); } }; ``` 2. 工厂方式 优点:可以多次创建对象实例,对象可以被复用。 缺点:无法实现对象的封装和继承。 示例代码: ```javascript function createPerson(name, age) { var obj = new Object(); obj.name = name; obj.age = age; obj.sayHello = function() { console.log('Hello, my name is ' + this.name + ', I am ' + this.age + ' years old.'); }; return obj; } var person1 = createPerson('Tom', 18); var person2 = createPerson('Jack', 20); ``` 3. 构造函数方式 优点:可以实现对象的封装,可以多次创建对象实例。 缺点:无法实现对象的继承。 示例代码: ```javascript function Person(name, age) { this.name = name; this.age = age; this.sayHello = function() { console.log('Hello, my name is ' + this.name + ', I am ' + this.age + ' years old.'); }; } var person1 = new Person('Tom', 18); var person2 = new Person('Jack', 20); ``` 4. 原型方式 优点:可以实现对象的继承,对象的属性和方法可以被多个实例共享,代码简洁。 缺点:对象的属性和方法被共享,可能会出现意外的修改。 示例代码: ```javascript function Person() {} Person.prototype.name = 'Tom'; Person.prototype.age = 18; Person.prototype.sayHello = function() { console.log('Hello, my name is ' + this.name + ', I am ' + this.age + ' years old.'); }; var person1 = new Person(); var person2 = new Person(); ``` 5. 组合方式 组合方式是将构造函数方式和原型方式结合起来使用,既可以实现对象的封装,也可以实现对象的继承。 示例代码: ```javascript function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHello = function() { console.log('Hello, my name is ' + this.name + ', I am ' + this.age + ' years old.'); }; var person1 = new Person('Tom', 18); var person2 = new Person('Jack', 20); ``` 总体来说,不同的创建对象方式各有优缺点,应根据具体的需求来选择合适的方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值