资讯专栏INFORMATION COLUMN

JavaScript的this值

charles_paul / 1955人阅读

摘要:函数的调用者是异步函数,不会为回调函数指定值。值为表示在全局对象下执行该函数执行方法,返回一个函数作为回调函数,并且将需要向它传递的作为参数。

重要

JavaScript的this是有函数求值是的调用者决定的

JavaScript的this是有函数求值是的调用者决定的

JavaScript的this是有函数求值是的调用者决定的

函数中的this

函数中的this在调用时才有意义,函数的调用者决定函数中this的指向,每个函数调用时都会有this属性。

function Point2D(x, y) {
  this.x = x;
  this.y = y;
}
Point2D.prototype.showLength = function() {
  var length = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
  console.log(length);
};
Point2D.prototype.showLengthAsync = function() {
  var self = this;   // 将this的值保存下来,传入异步函数

  setTimeout(function() {  // setTimeout是异步函数,在调用回调函数时没有指定匿名函数的this,
      self.showLength();  //因为回调函数没有指定this,非严格模式下默认指向全局对象
  }, 1000);
};

var x = 30, y = 40;
var p = new Point2D(3, 4);
var f = Point2D.prototype.showLength;

1.f():输出50,因为函数Point2D.prototype.showLength的调用者是变量f,变量fxyPonit2D函数在同一个对象中。

所以函数Point2D.prototype.showLengththis指向的是包含变量fxyPonit2D函数的对象。

this.x = 30this.y = 40,所以f()是输出50;

        Point2D.prototype.showLength = function() {
              var length = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
              console.log(length);
            };

2.setTimeout(p.showLength, 500); :延时500ms后输出50。函数p.showLength的调用者是异步函数setTimeoutsetTimeout不会为回调函数指定this值。

在非严格模式下,p.showLength中的this默认指向全局变量

this.x = 30this.y = 40,所以setTimeout(p.showLength, 500)延时500ms后输出50;

借用bind()方法可以将函数绑定到对象上,即将函数内的this指向bind(p)方法中传入的p对象

setTimeout(p.showLength.bind(p), 500);    // 延时500ms后输出5,因为函数p.showLength指向对象p,p的x属性为3,y属性为4

3.p.showLengthAsync();: 延时1000ms后输出5

Point2D.prototype.showLengthAsync = function() {
  var self = this;   // 将this的值保存下来,传入异步函数

  setTimeout(function() {  // setTimeout是异步函数,在调用回调函数时没有指定匿名函数的this,
      self.showLength();  //因为回调函数没有指定this,非严格模式下默认指向全局对象
  }, 1000);
};

p.showLengthAsync函数的调用者是p对象本身,由于setTimeout不会为回调函数指定this值。所以在p.showLengthAsync函数中使用变量selfp.showLengthAsync函数调用的this值(即对象p)保存下来

回调函数中才可以利用变量self访问到对象p,完成正确调用;

4.箭头函数

箭头函数很特殊,箭头函数中没有定义this,所以可以使用外层函数的this

Point2D.prototype.showLengthAsync = function() {
  setTimeout( () => this.showLength(), 1000);   // 箭头函数没有定义this,可以访问外层函数的this
};
call()

函数的call()方法可以指定函数调用时的this。函数中调用时的this值是可以改变的

并且,call()方法可以从第二个参数开始,指定传入调用函数的参数,以逗号分隔(多个原始形式的参数)

function Point2D(x, y) {
  this.x = x;
  this.y = y;
}
Point2D.prototype.showLength = function() {
  var length = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
  console.log(length);
};

var p = new Point2D(1, 1);
console.log(p.showLength());  // ==> 1.4142...   调用p.length方法的是p对象本身,所以this.x和this.y均等于1

var obj = {x: 3, y: 4};
console.log(p.showLength.call(obj));   // ==> 5  使用函数p.showLength的call()方法改变函数调用时this的指向,
                                       //使其指向对象obj,所以this.x=3,this.y=4

function foo() {
  // 使用Array.prototype.slice函数的call()方法指定函数调用时的this指向arguments对象,将其切分为数组
  var args = Array.prototype.slice.call(arguments);

  console.log(Array.isArray(arguments));  // false
  console.log(Array.isArray(args));   //  true
}

call()的作用:改变当前函数调用时的this

apply()

函数的apply()方法与call()方法作用完全一致,只是在调用时传入的参数有区别:

call()方法:第一个参数接收新的this值,后面的参数逗号分隔,逐个排列,传入调用函数中

apply()方法:第一个参数接收新的this值,第二个参数接收一个数组,将数组整体作为参数传入调用函数中

// 定义一个函数,实现一种变换,将传入其中的函数中参数的顺序颠倒
function __reverseArgs__(fn) {
  return function() {
    var args = Array.prototype.slice.call(arguments);
    
    return fn.apply(this, args.reverse());  // 这里的this表示这个匿名函数的调用者
  };
}
var foo = function() {console.log(Array.from(arguments));};
var foo2 = __reverseArgs__(foo);
foo2(1, 2, 3, 4);

注意return fn.apply(this, args.reverse());中的this。因为__reverseArgs__(fn)函数返回一个新的函数,apply()中的this指向调用这个新函数的对象。

foo2接收了__reverseArgs__(foo)返回的新函数,调用foo2(1, 2, 3, 4)时,因为foo2在全局对象下,所以this的值是全局对象。

__reverseArgs__(fn)方法应该只改变传入函数中参数的顺序,不改变原来函数调用的作用域。所以使用this将函数的调用改回传入函数的作用域。

bind()

bind()方法与call()apply()方法的最大区别在于返回值:call()apply()都会立即执行,返回结果;bind()方法会返回一个函数,并且可以通过bind()向函数中传递已经确定的参数,对于异步调用很有帮助。

function add(x, y) {
  return x + y;
}
// call()和apply()会立即执行;bind()方法会返回一个函数对象
console.log(add.call(null, 1, 2));   // ==> 3 ,使用call()方法在全局对象下对传入的参数1,2执行函数
console.log(add.apply(null, [1, 2]));   // ==> 3 apply()方法在全局对象下对传入的参数1,2执行函数
console.log(add.bind(null, 1, 2));   // ==> function () { [native code] }, 返回一个函数
// 传入两个参数
let add1 = add.bind(null, 1, 2);  // bind()方法执行时,将1和2对应传递给x,y,返回一个函数。再调用返回的函数时,不用再传递参数
console.log(add1());         //  ==> 3  为传递参数,直接调用,使用bind()时传入的参数值

// 传入一个参数
let add2 = add.bind(null, 1);   //bind()时只传递一个参数1给x,返回一个函数,在调用返回函数时只需炫迪一个参数给y即可
console.log(add2(3));    //  ==> 4  只需传递一个参数给y即可

// 不传递参数
let add3 = add.bind(null);    //调用bind()时不传递参数,返回一个函数,在调用返回函数时要传递2个参数
console.log(add3(2, 3));   // ==> 5  需要传递两个参数,与调用原函数没有区别。。。不建议使用

可以看出bind()的最大用处在于返回一个函数,并且可以向函数内传递确定的参数值(在bind()执行时时便已经确定的参数)。调用返回的函数时,无需再传入bind()时传入的参数---实现函数的部分调用

应用场景:类似于setTimeout(fn, 1000);的异步函数,需要一个函数作为参数,在1s后执行。有时对于已经知道fn调用时传入的参数值时,便可以使用bind(),先将参数传递进去,返回一个函数;等待时间到后,无需再向该函数传入参数,立即执行即可

function setBodyState(state) {
  document.body.className = state;
}
setBodyState.call(null, "state1");   // 立即执行setBodyState函数,将document.body.className设置为"state1"。
                                    // this值为null表示在全局对象下执行该函数
setTimeout(setBodyState.bind(null, "state2"), 1500);  // 执行bind()方法,返回一个函数作为回调函数,并且将需要向它传递
                                      //的"state2"作为参数。1500ms后立即执行返回的函数即可,无需再传入参数

bind()方法在异步函数中的应用,主要由于其返回一个函数,并且可以传入参数值的特性,可以减少异步函数调用的部分问题。

高阶函数实例 Closure中访问外部函数的argumentsthis、形参和局部变量
// __multi__()抽象一个过程,将传入的函数进行扩展,使其第一个参数接收类数组
// 调用原来的方法fn对每个第一个数组参数中的每个元素执行fn方法
function __multi__(fn) {
  return function(arrayLike, ...args) {
    return Array.from(arrayLike).map(item => fn(item, ...args));
  };
}

function __multi__(fn) {
  return function(arrayLike, ...args) {   // 返回一个函数(创建一个Closure),返回的函数接收夜歌类数组对象和rest参数作为参数
    return Array.from(arrayLike).map(function(value) {
      return fn(value, ...args);
    });
  };
}

function add(x, y) { return x + y;}
var add2 = __multi__(add);
console.log(add2([1,2,3], 4));  // ==> [5, 6, 7]

1.注意map()方法如果需要返回值,一定要在传入map()的函数中返回值,因为默认的返回值是undefined`
2.Closure中对于外层函数的局部变量、形参、实参对象argumentsthis的访问问题:

__multi__()返回一个函数(创建一个Closure),返回的函数保留对外层函数作用域的访问能力

this值和实参对象arguments是根据调用函数时确定的,如果要在Closure中访问到调用时的thisarguments对象,需要将其保存在外部函数局部作用域的变量中。

function __multi__(fn) {
  var self = this;
  var outerArgs = arguments;
  return function(arrayLike, ...args) {
    return Array.from(arrayLike).map(function(value) {
      console.log(self);    // 可以访问到外层函数的调用者
      console.log(outerArgs);  //可以访问到外层函数的实参列表,相当于访问外层函数的局部变量
      
      console.log(this);   // 访问到的this值是返回函数的调用者
      console.log(arguments);  // 访问到的arguments是传入返回函数的实参
      
      return fn(value, ...args);   // 可以访问到外部函数的形参  
    });
  };
}

Closure中可以访问到外部函数的形参(形参的特性与局部变量相同)

function __multi__(fn, a = 2) {
  console.log(arguments[0]);
  return function(arrayLike, ...args) {
    return Array.from(arrayLike).map(function(value) {
      console.log(a);    // 可以访问到外层函数的形参a
      return fn(value, ...args);   // 可以访问到外部函数的形参fn
    });
  };
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82074.html

相关文章

  • JavaScript进阶之’this

    摘要:所以相同点是,在全局范围内,全局变量终究是属于老大的。只生效一次引入了。只生效一次在箭头函数中,与封闭词法环境的保持一致。我通常把这些原始函数叫做构造函数。在里面你可以嵌套函数,也就是你可以在函数里面定义函数。 showImg(https://img-blog.csdnimg.cn/20190522000008399.jpg?x-oss-process=image/watermark,...

    shenhualong 评论0 收藏0
  • 理解JavaScript核心知识点:This

    摘要:关键字计算为当前执行上下文的属性的值。毫无疑问它将指向了这个前置的对象。构造函数也是同理。严格模式无论调用位置,只取显式给定的上下文绑定的,通过方法传入的第一参数,否则是。其实并不属于特殊规则,是由于各种事件监听定义方式本身造成的。 this 是 JavaScript 中非常重要且使用最广的一个关键字,它的值指向了一个对象的引用。这个引用的结果非常容易引起开发者的误判,所以必须对这个关...

    TerryCai 评论0 收藏0
  • JavaScript深入浅出第1课:箭头函数中this究竟是什么鬼?

    摘要:箭头函数没有自己的值,箭头函数中所使用的来自于函数作用域链。使用箭头函数打印对于内层函数,它本身并没有值,其使用的来自作用域链,来自更高层函数的作用域。 《JavaScript 深入浅出》系列: JavaScript 深入浅出第 1 课:箭头函数中的 this 究竟是什么鬼? JavaScript 深入浅出第 2 课:函数是一等公民是什么意思呢? 普通函数与箭头函数 普通函数指的是...

    MRZYD 评论0 收藏0
  • 关于JavaScriptthis理解

    摘要:想想也是难以置信,这应该全归功于对框架的依赖,促使助长了自己对学习的懈怠。真正的成了离职就失业的尴尬境地。我们接下来来了解下中的的使用和作用。以前对中的理解很简单粗暴谁调用就指向谁。如果例题中有不对的地方希望予以指教留言评论 前言 使用JavaScript有很长一段时间了,但是以前过多都是使用,从不去及理解其中原理,单单只是去生拼硬凑。这样的开发居然做了2年。匪夷所思居然项目中Java...

    lindroid 评论0 收藏0
  • 如何理解JavaScriptthis关键字

    摘要:原文链接参考深入理解原型和闭包完结王福朋博客园中的作用域详解博客园 前言 王福朋老师的 JavaScript原型和闭包系列 文章看了不下三遍了,最为一个初学者,每次看的时候都会有一种 大彻大悟 的感觉,而看完之后却总是一脸懵逼。原型与闭包 可以说是 JavaScirpt 中理解起来最难的部分了,当然,我也只是了解到了一些皮毛,对于 JavaScript OOP 更是缺乏经验。这里我想总...

    Yangder 评论0 收藏0
  • javascript对象不完全探索记录02:疯狂打call!给谁打call?打什么call?

    摘要:注意该方法的作用和方法类似,只有一个区别,就是方法接受的是若干个参数的列表,而方法接受的是一个包含多个参数的数组。指定的参数列表。返回值返回值是你调用的方法的返回值,若该方法没有返回值,则返回。 温馨提示:作者的爬坑记录,对你等大神完全没有价值,别在我这浪费生命温馨提示-续:打call原本是属于我们偶像宅文化中的专业名词,指的是饭们在看live时在台下配合爱豆演出的节奏喊口号,举例:超...

    Shimmer 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<