object取值_this的原理、函数的不同调用方式this取值、以及不同环境下this的取值、函数四种调用方法...
一、this的原理
1.1问题的由来
学懂 JavaScript 语言,一个标志就是理解下面两种写法,可能有不一样的结果
var obj = {foo: function () {} };var foo = obj.foo;// 写法一 obj.foo()// 写法二 foo()上面代码中,虽然obj.foo和foo指向同一个函数,但是执行结果可能不一样。请看下面的例子。
var obj = {foo: function () { console.log(this.bar) },bar: 1 };var foo = obj.foo; var bar = 2;obj.foo() // 1 foo() // 2这种差异的原因,就在于函数体内部使用了this关键字。很多教科书会告诉你,this指的是函数运行时所在的环境。对于obj.foo()来说,foo运行在obj环境,所以this指向obj;对于foo()来说,foo运行在全局环境,所以this指向全局环境。所以,两者的运行结果不一样。
函数的运行环境到底是怎么决定的?举例来说,为什么obj.foo()就是在obj环境执行,而一旦var foo = obj.foo,foo()就变成在全局环境执行?
理解了这一点,你就会彻底理解this的作用。
1.2内存的数据结构
JavaScript 语言之所以有this的设计,跟内存里面的数据结构有关系。
var obj = { foo: 5 };上面的代码将一个对象赋值给变量obj。JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量obj。
也就是说,变量obj是一个地址(reference)。后面如果要读取obj.foo,引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。
原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。举例来说,上面例子的foo属性,实际上是以下面的形式保存的。
{foo: {[[value]]: 5[[writable]]: true[[enumerable]]: true[[configurable]]: true} }注意,foo属性的值保存在属性描述对象的value属性里面。
1.3函数
这样的结构是很清晰的,问题在于属性的值可能是一个函数。
var obj = { foo: function () {} };这时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo属性的value属性。
{foo: {[[value]]: 函数的地址...} }由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。
var f = function () {}; var obj = { f: f };// 单独执行 f()// obj 环境执行 obj.f()1.4环境变量
JavaScript 允许在函数体内部,引用当前环境的其他变量。
var f = function () {console.log(x); };上面代码中,函数体里面使用了变量x。该变量由运行环境提供。
现在问题就来了,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。
var f = function () {console.log(this.x); }上面代码中,函数体里面的this.x就是指当前运行环境的x。
var f = function () {console.log(this.x); }var x = 1; var obj = {f: f,x: 2, };// 单独执行 f() // 1// obj 环境执行 obj.f() // 2上面代码中,函数f在全局环境执行,this.x指向全局环境的x。
在obj环境执行,this.x指向obj.x。
obj.foo()是通过obj找到foo,所以就是在obj环境执行。一旦var foo = obj.foo,变量foo就直接指向函数本身,所以foo()就变成在全局环境执行。
二、函数不同调用方式中this取值
JavaScript 中的 this,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript 中函数的调用有以下几种方式:
- 作为对象方法调用
- 作为函数直接调用
- 作为构造函数调用
- 使用 apply 或 call,bind 调用
我们将按照调用方式的不同,分别讨论 this 的含义。
2.1作为对象方法调用
在 JavaScript 中,函数也是对象,因此函数可以作为一个对象的属性,此时该函数被称为该对象的方法,在使用这种调用方式时,this 被自然绑定到该对象
var test = { a:0, b:0,get:function(){return this.a; } } console.log(test.get()) //0var prop=30 var O={prop:37} function independent(){return this.prop } O.f=independentconsole.log(O.f()) //37 作为对象方法调用console.log(independent()) //30 作为函数直接调用由此可见this是动态作用域,this的指向关键是看函数怎么调用而不是怎么创建
2.2作为函数直接调用
函数也可以直接被调用,此时 this 绑定到全局对象(在严格模式下为undefined)。在浏览器中,window 就是该全局对象。
function fn1 (){return this } console.log(fn1()) //windowfunction fn2 (){"use strict";//see strict modereturn this } console.log(fn2 ()) //undefined2.3作为构造函数调用
- 当函数作为构造函数调用时,this代表new出来的对象,并且对象的原型会指向构造器的prototype(构造器是Foo、foo.constructor=Foo )
- 如果没有使用new而是直接调用函数,this===window;
2.4使用 apply 或 call,bind 调用
apply 和 call允许切换函数执行的上下文环境(context),即 this 绑定的对象。this取得的是apply() 、call()、 bind()调用时传入的第一个参数(bind是ES5提供的方法,所以只有IE9及以上才支持)
2.4.1 apply()、call()
function add(c,d){return this.a + this.b +c + d; }var O = {a:1,b:3}add.call(O,5,7); //1+3+5+7add.apply(O,[10,20]) //1+3+10+20function bar(){console.log(Object.prototype.toString.call(this)) }bar.call(7) // "[object Number]"2.4.2 bind()改变this取值
function f(){return this.a }var g = f.bind({a:"test"}) console.log(g()); //testvar O = {a:37,f:f,g:g}; console.log(O.f(),O.g()) //37,testbind与new(用new调用bind改变不了this取值)
function foo(){this.b=100;return this.a } var func = foo.bind({a:1}) func(); //1 new func(); //{b:100}直接调用func(),this会指向bind中的参数,所以返回值为1,由于执行this.b=100,所以this对象会有个b属性,最终this为{a: 1, b: 100}。用new的话return如果不是对象会把this作为返回值,并且this会被初始化为一个默认的空对象,对象的原型是构造器的prototype,所以这里虽然用了bind的方法,但是this仍然会指向没有bind的情况。this指向空对象,空对象的原型指向foo.prototype,空对象的b属性会设置为100,整个对象会作用返回值(return this.a 被忽视)所以结果为{b:100}.
bind与currying(函数柯里化:把一个函数拆成多个单元)
function add(a,b,c){return a + b + c }var func = add.bind(undefined,100); func(1,2) //103 (100 + 1 +2)var func2 = func.bind(undefined,200); func2(10) //310 (100+ 200 + 10)有时候不需要一次性把函数都调用完,而是调用一次把前几个参数传完了以后得到了一个函数再去调用,并且每次都传入自己所需要传入的值。
add.bind不需要改变this,那么就传入undefined(null也可),但是我们提供了额外参数100,那么bind方法就会把100赋值给a,再调用的时候1会给b,2就会给c,所以最后答案是103。再func.bind一次a上次绑定了100,b就会绑定为200,再调用func2传入值为10,10赋值为c,所以func2调用结果为310。
function getConfig(color,size,otherOptions){console.log(color,size,otherOptions) }var defaultConfig=getConfig.bind(null,"#cc0000","1024*768");defaultConfig("123") //#cc0000","1024*768,123 defaultConfig("456") //#cc0000","1024*768,456三、不同环境this的取值
3.1全局作用域中this(浏览器)
全局作用域中的this一般指向全局对象,在浏览器中全局对象就是window
console.log(window.document===document) //true console.log(this===window) //true this.a=37 console.log(window.a) //373.2对象原型链上的this
var o={ f:function(){ this.a + this.b } } var p=Object.create(o) p.a=1 p.b=4 console.log(p.f()) //5p是空对象并且它的原型会指向o(p.__proto__===o),调用原型上的方法(对象本身没有此方法会沿着原型链向上查找,原型链上的this也可以指向Object.create创建的对象,此this的指向与new创建实例对象的指向类似)this.a和this.b仍然能取到对象p上的a和b。即原型链上的this可以指向所在的对象(p对象)
注:p的构造器不为o,为Object(p.constructor===Object),这是用Object.create创建对象与new实例创建对象的不同,如果是new创建的实例对象构造器就是o。
3.3get/set方法与this
此时this的指向与原型链上的this的指向类似
function modules(){return Math.sqrt(this.re*this.re + this.im*this.im) }var o={re:1,im:-1,get phase(){return Math.atan2(this.im,this.re)} }Object.defineProperty(o,"modules",{get:modules,enumerable:true,configurable:true })console.log(o.phase,o.modules) //logs -0.781.4142get、set方法中的this,一般情况下也是会指向get,set方法所在的对象(与一般对象属性作为函数对象类似)。
参考:
JavaScript 的 this 原理
https://blog.csdn.net/qq_40646986/article/details/82657909
总结
以上是生活随笔为你收集整理的object取值_this的原理、函数的不同调用方式this取值、以及不同环境下this的取值、函数四种调用方法...的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: python --version没输出_
- 下一篇: c语言延时函数_SystemVerilo