new关键字,call/apply/bind方法都和this的绑定有关,在学习之前,首先要理解this。

一起来学习一下this吧

首先。this是一个对象。

对象很好理解,引用类型值,可以实现如this.xxx、this.xxx()等等的操作。

验证:

接着,this只有当执行上下文创建执行时,才会被绑定。

在全局执行上下文中。(当浏览器碰到有JS代码要执行时,就会创建全局执行上下文,可以简单理解为打开网页就创建了)

this指向window对象

验证:

 <script>
console.log(this === window);// true
</script>

函数内的this,只有当函数被调用时才会被绑定。

首先,一般的函数内部都有this这个特殊的对象。(箭头函数除外)

调用函数,引擎工作机制是创建一个函数执行上下文,进入函数执行上下文,执行这里的代码。(这里涉及到了执行栈、变量对象,作用域链等等)

不看上面那段话也很好理解,代码演示:

 function fn() {
console.log(this);
}

以上代码声明了一个函数fn,fn函数做的事情就是在控制台输出this。

只有fn函数被调用时,我们才知道函数内部的this指向何处。

那this究竟指向何处呢(箭头函数单独讨论,以下函数都是指function关键字声明的函数)

第一种情况,当函数被普通调用时(如:fn()),this指向window对象。

代码演示:

 fn(); // window

这种情况下,不管函数嵌套的有多深。this指向的都是window。验证:

 function fn() {
console.log(this);
}
const obj = {
fn: function(fn) {
fn();
}
}
obj.fn(fn);// window

第二种情况,当函数被new 关键字调用时(如:new Fn()),this指向new关键字新创建的对象。

代码演示:

 function Fn() {
console.log(this);
window.myCheckThis = (obj) => {
console.log(this === obj);
}
}
const fn = new Fn();
myCheckThis(fn);// true

以上代码通过new关键字调用Fn函数后,返回一个对象。保存在fn中。

在函数内部定义了一个全局的方法,检查函数内部的this指向。

这里利用了箭头函数没有this这个特性,以及闭包的特性。(在函数内部创建一个函数,那么这个函数可以访问外层函数作用域链上的变量)

通过作用域链访问到了外层函数的this。验证结果输出true。

也可以只通过闭包实现这种验证。代码演示:

 function Fn() {
const self = this;
console.log(this);
window.myCheckThis = function(obj) {
console.log(self === obj);
}
}
const fn = new Fn();
myCheckThis(fn);// true

以上代码,把外层函数的this保存在self中。内层函数通过作用域链就可以访问到这个这个变量。

注意,内层函数直接访问的this。是内层函数自身的this。

第三种情况,当函数被call--apply--bind方法调用时(如:fn.call(obj)),this指向这些方法绑定的对象。

在实际中,函数的this会指向这些方法的第一个参数。

当参数为null,undefined,window时,this指向window。

当参数是基本类型值时,指向该基本类型值的包装类型值。代码演示:

 function fn() {
console.log(this);
}
fn.call();// window,apply、bind同理
fn.call('fn'); // String {"fn"},其他基本类型值、apply、bind同理

以上都不是重点,emmmm.....

当参数是引用类型值时,指向该对象。代码演示:

function fn() {
console.log(this);
}
fn.call({name: 'xm'});// {name: "xm"},apply、bind同理。

需要注意的是:bind方法返回一个函数,apply方法第二个参数是数组或类数组对象。

代码演示:

function fn() {
console.log(this);
}
const fn1 = fn.bind({name: 'xm'});
fn1();// {name: "xm"}
 function add(a, b) {
console.log(a + b);
}
add.apply(null, [1, 2]);//

这三个方法还有很多别的用途,最常见的莫过于把类数组对象转换成数组对象了。

代码演示:

 function fn(a, b, c, d) {
const args = Array.prototype.slice.call(arguments, 0);
return args.reduce((_a, _b) => {
return _a + _b;
}, 0);
}
console.log(fn(1, 2, 3, 4));//

以上函数内部属性arguments对象是类数组对象,.slice()方法是在Array原型对象创建的方法,

该方法内部this指向Array实例,通过call()方法,把this指向了arguments。

第二行代码可以简单理解为arguments.slice(0),返回一个新的数组对象。

然后在调用数组的reduce()方法。

第四种情况,当函数作为对象的方法调用时,this指向该对象。

这是很常见的一个情况,代码演示:

 const obj = {
name: 'xm',
say: function () {
console.log(this.name);
}
}
obj.say();// xm

第五种情况,箭头函数的this。

箭头函数本身没有this。在函数内部访问this。会沿着函数的作用域链往外查找this。

代码演示:

 const obj = {
say: () => {
console.log(this);
}
}
obj.say();// window

以上在obj对象上创建了say方法。由于采用了箭头函数的书写方式,函数内部没有this。

在内部访问this时,会沿着作用域链往外查找。往外就是全局执行上下文了,这里的this就是window。

在看下面的例子:

 const obj = {
say: function() {
return () => {
console.log(this);
}
}
};
const fn = obj.say();
fn();// obj

以上代码调用obj.say()方法,返回匿名的箭头函数,保存在fn中,接着调用fn()函数。

由于箭头函数没有this,在内部访问this会沿着作用域链往外查找,它的外层函数是obj.say()函数。

该函数在第八行被调用时,this绑定了obj对象。因此箭头函数的this也是指向obj。

如果say方法也是箭头函数,会继续往外层找,那时就是指向window了。

在看一个例子:

 const obj = {
say: function() {
return () => {
console.log(this);
}
}
};
const say = obj.say;
const fn = say();
fn();// window

以上代码,第九行普通调用say函数,this指向window。因此箭头函数的this也是指向window。

那么箭头函数与普通函数(function)的区别是什么?构造函数(function)可以使用 new 生成实例,那么箭头函数可以吗?为什么?

箭头函数是普通函数的另一种写法(简短的写法)。书写方式不同是第一大区别。

箭头函数内部没有arguments对象,如果要用,可以用 rest 参数代替。即(...rest) =>{console.log(rest)}。

箭头函数内部没有this对象。

箭头函数不能通过new操作符调用。(因为箭头函数没有自己的this。也无法调用call/apply方法。也没有prototype属性。无法给实例添加指向构造函数原型对象的原型指针)

箭头函数使用 yield 操作符。(生成器函数返回操作)

看到这里,想必已经了如指掌,学有所成,胸有成竹... emmmm....

接下来让我们实战一下吧,看看面试题是如何挖坑的。

 const length = 10;
function fn() {
console.log(this.length);
}
const obj = {
length: 5,
method: function(fn) {
fn();
arguments[0]();
}
};
obj.method(fn, 1);

点击查看答案

这里主要考察函数普通调用时this的指向、作为对象的方法调用时this的指向,以及var、let、const声明变量的区别,加上对window对象、arguments对象的了解程度。

首先,第一行代码在全局作用域中声明了length变量。值为10。

在全局作用域下,var声明变量会成为window对象的属性,如有同名属性则覆盖。

let、const声明的变量不在同一作用域(会产生自己的块级作用域,在作用域中直接通过标识符访问,不能用window.xxx访问),则没有这种功效了。

如图:

接着看第十二行,调用obj.method()方法。传入两个参数,第一个参数是函数fn。

然后开始执行第八行,调用fn()函数,此时函数仅仅是普通调用,则函数内部的this指向window。

接着开始执行第九行,注意,此时函数是作为arguments对象的方法调用,函数内部的this指向arguments对象。

非常具有迷惑性,控制台输出下arguments对象一目了然。

JavaScript内置一些方法的实现原理--new关键字,call/apply/bind方法--前戏的更多相关文章

  1. JavaScript内置一些方法的实现原理--new关键字,call/apply/bind方法--实现

    先学习下new操作符吧 new关键字调用函数的心路历程: 1.创建一个新对象 2.将函数的作用域赋给新对象(this就指向这个对象) 3.执行函数中的代码 4.返回这个对象 根据这个的思路,来实现一个 ...

  2. JavaScript内置一些方法的实现原理--Object.freeze()、instanceof

    const定义的常量,一般是不能修改的. 比如: const TIME_OUT = 10000; 但是当值为引用类型值时,还是可以操作对象,扩展或修改对象属性.方法等等. 以下演示代码的操作是不会报错 ...

  3. Atitit paip.对象方法的实现原理与本质.txt

    Atitit paip.对象方法的实现原理与本质.txt 对象方法是如何实现的1 数组,对象,字典1 对象方法是如何实现的 这显然是一个对象方法调用.但对象方法是如何实现的呢?在静态语言中,因为有编译 ...

  4. 第7天-javascript内置对象

    数组相关方法 concat 用来连接多个数组 <script> var a = [1,2,3]; var b = [3,4,5]; var c = a.concat(b); console ...

  5. JavaScript进阶 - 第7章 JavaScript内置对象

    第7章 JavaScript内置对象 7-1 什么是对象 JavaScript 中的所有事物都是对象,如:字符串.数值.数组.函数等,每个对象带有属性和方法. 对象的属性:反映该对象某些特定的性质的, ...

  6. JavaScript 内置函数有什么?

    javaScript内置函数 1.Date:日期函数 属性:constructor 所修立对象的函数参考prototype 能够为对象加进的属性和方法 方法:getDay() 返回一周中的第几天(0- ...

  7. javascript内置函数提供的显式绑定

    内置函数提供的显式绑定 最近在开发中遇到使用arr.map(module.fun) 这样的写法时(在一个模块调用了另外一个模块的方法), 造成了函数中this丢失的问题, 显示为undefined, ...

  8. JavaScript中的内置对象-8--1.Array(数组)-Array构造函数; 数组的栈方法; 数组的转换方法; 数组的操作方法; 删除-插入-替换数组项; ECMAScript为数组实例添加的两个位置方法;

    JavaScript内置对象-1Array(数组) 学习目标 1.掌握任何创建数组 2.掌握数值元素的读和写 3.掌握数组的length属性 如何创建数组 创建数组的基本方式有两种: 1.使用Arra ...

  9. document和javaScript内置对象

    1.Document 属性: referrer //返回载入当前文档的URL URL //返回当前文档的URL 方法: getElementById(); //根据id获取html元素对象 getEl ...

随机推荐

  1. 一、jenkins下载及安装

    一.安装 官网地址:https://jenkins.io/zh/ 1.下载war包,放到tomcat——>webapps下,双击bin——>startup.bat启动 2.打开命令提示符. ...

  2. 设置pycharm文件默认换行符onfiguring Line Separators

    http://www.jetbrains.com/help/pycharm/2016.2/configuring-line-separators.html PyCharm makes it possi ...

  3. spring 依赖注入的3种方式

    在实际环境中实现IoC容器的方式主要分为两大类,一类是依赖查找,依赖查找是通过资源定位,把对应的资源查找回来:另一类则是依赖注入,而Spring主要使用的是依赖注入.一般而言,依赖注入可以分为3种方式 ...

  4. Swift4.0复习基本语法简介

    1.五种类型: 包含五种类型——枚举(enum).结构体(struct).类(class).协议( protocol)以及函数类型(function types). 2.变量对象:var 3.常量对象 ...

  5. JS中search查找某些内容,正则表达式|查找分隔的任何项

    JS中可以用indexOf来查找某个字符串里的某些内容的索引,也就是在字符串的位置.如果存在该字符串,会返回该字符串的索引,如果不存在会返回-1,可以通过某些内容的索引是否为-1判断是否存在该字符串. ...

  6. C#Json数据反序列化为Dictionary并根据关键字获取指定的值

    Json数据: { "dataSet": { "header": { ", "errorInfo": "HTTP请求错误 ...

  7. 常用HTML转义字符,html转义符,JavaScript转义符,html转义字符表,HTML语言特殊字符对照表(ISO Latin-1字符集)

    HTML字符实体(Character Entities),转义字符串(Escape Sequence) 为什么要用转义字符串? HTML中<,>,&等有特殊含义(<,> ...

  8. RabbitMQ官方教程二 Work Queues(GOLANG语言实现)

    RabbitMQ官方教程二 Work Queues(GOLANG语言实现) 在第一个教程中,我们编写了程序来发送和接收来自命名队列的消息. 在这一部分中,我们将创建一个工作队列,该队列将用于在多个wo ...

  9. 感受typescript定义变量和数据类型的神奇魔力

    变量和数据类型 你的Javascript能力到达瓶颈?那是因为你还不会typescript.掌握TS,让你的开发更加准确简洁. 今天的学习中,我们接着从TS的数据类型和变量入手,感受它们的奇妙魔力. ...

  10. Mowing the Lawn【线性dp + 单调队列优化】

    题目链接:https://ac.nowcoder.com/acm/contest/2652/G 题目大意:与上一篇博客 烽火传递 差不多. 1.一共n头羊,若超过m头连续的羊在一起,就会集体罢工,每头 ...