一. 纲

this的性质

  1. 作用:表示函数执行时的环境

  2. 值:一个对象

  3. 特点:动态性

确定this的难度

  • JS语言的动态性:

函数的this在执行时才能确定

  • 函数为一级公民

可作实参、返回值、数据赋值进行传递

解决方法

  • 书写函数时,要预想该函数会被如何调用
  • 调用标识符确定this值
  • 以变量代替this、使用绑定

函数的调用标识符

1. 函数是复杂数据类型

2. 变量可获取函数值--引用地址

3. 函数调用时,该调用名字是该函数此次调用时的标识符

以标识符确定this值

唯一能确定this的途径:函数的调用标识符

  1. 未使用绑定字: 函数的调用标识符是否属于一个对象的成员

是:this就指向该对象

否:宽松模式是window;严格模式是未定义

  1. 使用绑定字 (执行时) : 把this绑定到指定对象上

开启严格模式

  • 函数体内使用严格关键词

  • class方法与模块方法自动运行在严格模式中

  • 不包括方法简写

// 伪代码
//以下fn均表示一個宽松环境的函数, obj表示一个对象
fn(); // window
obj1.fn(); // obj1
obj2.obj3.fn() // obj3
fn.call(obj4) // obj4
new fn() // 实例

函数fn执行时,其调用标识符均是fn

每个标识符fn所属的对象都不同,其this就不同

  • fn() : 调用标识符fn不属于任何对象:thiswindow

  • obj1.fn() : 调用标识符fn属於对象obj1的成员

    thisobj1

  • obj2.obj3.fn(): 调用标识符fn属於对象obj2的成员

    thisobj3

  • 未使用绑定时,函数的调用标识符属于对象的一个成员时,其this就是该对象

二. 未使用绑定字

确定 this 值 :

只要查看该函数调用时的标识符

该标识符决定当前函数执行时的this值

1 函数自我调用

  1. 调用标识符是该函数本身的标识符

  2. 该标识符不属于任何对象的属性

  3. 此时this指向

    宽松模式:全局对象window、global(node环境)·

    严格模式: this为未定义  

  4. 立即执行函数也是函数自我调用,

    其调用标识符是anonymou

    其this,永远是window/未定义

2 一个函数的多种调用形式

  1. 函数的引用地址被赋值给一个对象的属性

  2. 函数调用时: 若调用标识符属于对象的一个成员

  this就指向该对象

  1. 例:

    ​ 1.函数作为数据进行赋值

    // 伪代码: fn表示宽松模式的下的函数
    const A = {
    fn: fn
    };
    const B = {
    b1: A.fn,
    b2: A
    };
    var c = objA.fn;
    fn(); // window
    A.fn(); // A
    B.b1() // B
    B.b2.fn(); // A
    c(); // window

函数的调用属于地址引用,可赋值给不同变量

调用函数时的标识符 ,决定了此函数执行时的其内部的this值

​ 分析表

调用形式 调用标识符 标识符所属对象 this
fn0() fn0 window
A.fn() fn() A A
B.b1() b1() B B
B.b2.fn() fn() b2,b2的值是A A
c() c() window

​ 2. 函数作为数据进行赋值

    var obj = {
y: function fn1(x) {
console.log(this);
while (x) {
fn1(x - 1);
break;
}
},
z: function fn2(x) {
console.log(this);
while (x) {
obj.z(x - 1);
break;
}
},
};
obj.y(1); //obj 、 window"
obj.z(1); //obj 、window"
调用形式 调用标识符 标识符所属对象 this
obj.y(1) y() obj obj
fn1(x - 1) fn1() window
obj.z(1) z() obj obj
obj.z(x-1) z() obj obj

​ 3.函数作为参数传入

​ 1.参数传递是一种隐式赋值

​ 2.传参:类似于: argument = A.fn ;

​ 参数cb获取了函数fn的引用

// 伪代码: fn表示宽松模式的下的函数
let A = {
fn,
fn2: (function () {
console.log(this);
})()
},
B = {};
function b(cb) {
console.log(this); // window
B.fn = cb;
cb(); // window
B.fn(); // B
}
A.fn(); // A
b(fn);
调用形式 调用标识符 标识符所属对象 this
A.fn() fn() A A
b(fn) b() window
cb() cb() window
B.fn() fn() B B

​ 3.参数所属对象要看情况

​ 1.如在浏览器方法中的定时器

​ 其回调函数参数是属于window对象的成员

​ 回调函数thiswindow对象

var obj = {
fo: function fo() {
'use strict'
console.log(this);
}
};
obj.fo(); // obj
setTimeout(obj.fo, 100); // window

​ 2.如事件监听

​ 其回调参数所属对象是元素本身

  1. 其他

    1. getter 与 setter函数同理

    2. 实例中继承的方法同理

    3. class语法的super调用的方法中的this

      1.super是静态的,指向当前类的基类

      2.但是super调用的方法时,进行了绑定

      ​ 把this绑定到使用super·的类

    function fn() { };
let obj = {};
Function.prototype.f1 = function () {
console.log(this);
return function f2() {
console.log(this);
}
}
Function.prototype.x(); // Function.prototype
Function.x() // Function
obj.f = fn.f1(); //fn
obj.f(); // obj
fn.f1()(); // window
let f2 = fn.f1();
f2(); // window
调用形式 调用标识符 标识符所属对象 this
Fn.proto.x() x() Fn.proto Fn.proto
fn.f1() f1() fn fn

f2的this,同样只能在其被调用时决定

不在于它被赋值给谁

调用形式 调用标识符 标识符所属对象 this
obj.f() f() obj obj
fn.f1()() 匿名函数() window
f2() f2() window

3.事件处理

  1. 内联事件:

    1. 事件触发函数执行,该函数的调用标识符决定this

    2. 传入this实参

      内联事件中的this实参是该事件的元素本身

<button onclick="fn(this)">AMTF</button>
<button onclick="obj.fn(this)">JLSJ</button>
function fn(v) {
console.log(this, v); // window、 button-AMTF
}
let obj = { fn }; // obj、 button-JLSJ
document.getElementsByTagName("button")[0]
.onclick = function () {
console.log(this); // button-AMTF
}
  1. 外联事件与事件监听

    1. 相当于把函数赋值给该元素的某个属性

    2. 触发该函数执行时,调用者是这个元素

      this指向调用该函数的元素-- e.currentTarget

    btn.addEventListener('click', fn);
function fn(e) {
if (e.target == this) {
// 只在元素本身被觸發時執行
// doSomething...
console.log(e.target, this);
}
}
  1. IE的attachEvent的回调函数的this是window

    修改this:把函数赋值给对象,再执行这个对象的方法

function attachEvent (elm, event, cb) {
elm.attachEvent(("on" + event), function () {
elm.cb = cb;
elm.cb(window.event);
delete elm.cb;
});
}
// 或者使用绑定
function attachEvent(elm, event, cb) {
elm.attachEvent(("on" + event), cb.bing(elm, window.event));
}

三.使用绑定

确定this值: 绑定关键字指定对象即为this值

同样只能在函数执行时确定

四个绑定关键词: new、call、apply、bing

1.callapply

  1. 直接指定一个对象作为 this 的值

  2. 可使用apply来展开一个数组

    ES5:可传入类数组对象

2. 硬绑定bing

  1. 原理
function fakeBind(fn, obj) {
return function () {
return fn.apply(obj, arguments);
};
}
  1. 返回一个新函数
  2. 新函数与原函数的函数体相同
  3. 但其this被绑定到指定的对象
  1. 在使用了bing的函数是尚未执行的,

    this是不确定的: 只能在执行时确定

    使用new调用:为new创建的对象

    没使用new调用:为bing绑定的对象

    只会被new改变

3. 构造器的new绑定

一定會用新创建的实例对象替换原來的this

无论该函数是否已被其他绑定字绑定过

4. 自带绑定

JS、库、宿主环境的内置函数,提供一个参数

​ 用于绑定 this值

例:数组的forEach的参数2

5. 绑定到空/原始值

  1. 不错的空对象: Object.create(null);
  2. 绑定到null / undefined
  • 宽松模式:绑定到全局对象
  • 严格模式:null / undefined
  1. 绑定到原始值(字符串类型、布尔类型或者数字类型)会封装成对应的对象

new String、new Boolean、new Number

    let o = {
a: function () {
console.log(this);
}
}; o.a(); // o
o.a.call(null); // window
o.a.call(1); // Number(1)

四. 箭头函数

确定this值 :

箭头函数没有this值

通过引用其上属函数的this值

若该箭头函数没有上属函数,则其this值为未定义

  1. 无法使用绑定关键词修改箭头函数的this值

    因为他没有this值

  2. 唯一修改this值:

    给他套上普通函数,再修改该普通函数的this

function x() {
return (a) => {
//this 取自 x()
console.log(this.a);
};
}
var oA = { a: 2 };
var oB = { a: 3 };
var y = x.call(oA);
y.call(oB); // 2

由于xthis 绑定到 oA

箭头函数的this指向 oA: y的this 也指向 oA

  1. 确定箭头函数的this:

    • 没有所属函数时,只能是未定义
    • 有所属函数,取决于所属函数执行时的this

五.例

1.闭包函数

在一个函数中返回另一个函数

    function f1() {
console.log(this);
return function f2() {
console.log(this);
}
}
f1()();

 函数f1返回的函数的this值,由调用时决定

   f1( )( )·:两次this都是指向了window

​ f2执行时,如立即执行函数

    const obj = {};
obj.y = f1();
obj.y();
let fn = fn1();
fn();

​ 把f1的返回值,赋值给一个变量

  f2的this:调用时决定--fn()、obj.y()

  调用标识符所属的对象不相同

​ 其 f2的this也不相同

2. 反柯里化:

反柯里化:让对象去借用一个原本不属于它的方法

​ 普通函數寫法

    Function.prototype.uncurrying = function () {
var self = this;
return function () {
let thisArg = [].shift.call(arguments);
return self.apply(thisArg, arguments);
}
}

​ 箭頭函數寫法

    Function.prototype.uncurrying = function () {
return (...args) => this.call( ...args)
}
  1. 箭头函数的this是取值其外层函数的this,

  2. 外层函数uncurrying的this值,决定于调用时的

let push = Array.prototype.push.uncurrying();
  1. 此时uncurrying的调用者是push函数

则此时的this是指向push函数

3. bing的实现

  1. 工具函数
    objectCreate = Object.create || function (proto) {
var F = function () { };
F.prototype = proto;
return new F();
}
let slice = Array.prototype.slice.uncurrying();
let concat = Array.prototype.concat.uncurrying();
  1. 实现逻辑
    Function.prototype.bind = function (thisArg) {
if (typeof this !== "function") {
throw new TypeError("被绑定的不是函數");
} var self = this;
var baseArgs = slice(arguments, 1); var bind = function () {
return self.apply(
this instanceof self ? this : thisArg,
concat(baseArgs, slice(arguments))
);
}
bind.prototype = objectCreate(self.prototype);
return bing;
};

4. 自定义默认绑定对象

实现:修改例3的bind函數的返回值的apply的参数1

默认绑定--只当this指向全局对象或者undefined

   把this绑定到默认的对象

        return self.apply(
((!this || this === (window || global)) ?
thisArg : this
),
concat(baseArg, slice(arguments))
);

​ 对apply参数1的定制,可实现多种绑定

5. 多重綁定

  const obj1 = {
name: "obj1"
};
const obj2 = {
name: "obj2"
}; function f1() {
console.log(this);
}
function f2() {
f1.call(this);
}
const f3 = () => f2.call(this); f2.call(obj1); // obj1
f3.call(obj2); // window

f2 .call(obj1 )

f2的this被綁到了obj1,所以f1.call(this)的this是obj1;使得f1的this為obj1

f3.call(obj2)

f3是箭頭函數--this綁定無效,但其沒有所屬的函數,所以f2.call(this)的this值為未定義

​ 而在寬鬆模式下的綁定到未定義會改爲绑定到全局对象

6. 立即执行函数

    let o = {
a: (function () {
console.log(this); // window
return function () {
console.log(this); // 看情况:调用标识符决定
}
})()
};
(function (fn) {
console.log(this); // o
fn.call(this); // window
(() => console.log(this))(); // o
}).call(o, () => console.log(this));

立即执行函数的this

  • 未使用绑定时,永远是window/未定义
  • 执行时使用绑定,是被绑定的对象
  • 箭头函数,其this如同继承的属性,取自其所属的函数

7.函数式编程相关

在函数中返回一个对象,进行链式调用

    let x = function () {
let obj = { y, z }
return obj; // 等效于 return { y, z };
function y() { console.log(this); return this }
function z() { console.log(this); return this }
};
x().y().z();
  • 因为返回的是对象

    x() === obj ,而 x().y() === obj.y()

    x().y() === obj ,而x().y().z()=== obj.z()

通过调用标识符确定this的更多相关文章

  1. JSON-RPC轻量级远程调用协议介绍及使用

    这个项目能够帮助开发人员利用Java编程语言轻松实现JSON-RPC远程调用.jsonrpc4j使用Jackson类库实现Java对象与JSON对象之间的相互转换.jsonrpc4j包含一个JSON- ...

  2. JSON-RPC远程调用协议

    1. JSON-RPC简介 2. 请求 3. 响应 4. 错误 4.1. 错误对象 4.2. 错误码 5. 批量调用 6. 示例 6.1. 列表形式参数 6.2. key-value形式参数 6.3. ...

  3. js 闭包 理解

    1.什么是闭包 定义:是指有权访问另一个函数作用域中的变量的函数 创建闭包:在一个函数内部创建另一个函数 基本特点 在返回的匿名函数中 可以调用外部函数的变量 如下例中所示 内部函数(匿名函数) 可以 ...

  4. 关于C语言的一些trick

    很多东西已经记不起来了,想到一点写一点,碰到一点写一点,慢慢累积. 关于# #在宏定义中用于替换传入变量的字符,例如: #define whole_operation(n)  do { printf( ...

  5. Atitit.js javascript的rpc框架选型

    Atitit.js javascript的rpc框架选型 1. Dwr1 2. 使用AJAXRPC1 2.2. 数据类型映射表1 3. json-rpc轻量级远程调用协议介绍及使用2 3.1. 2.3 ...

  6. php static延迟静态绑定

    如果你是一个懒惰的程序员,你看到以下代码可能会恼火 abstract class U{ } class u1 extends U{ public static function create(){ r ...

  7. angularjs入门学习【指令篇】

    一.首先我们来了解下指令API 属性 含义 restrict 申明标识符在模版中作为元素,属性,类,凝视或组合,怎样使用 priority 设置模版中相对于其它标识符的运行顺序 Template 指定 ...

  8. [机翻] WIRER ON THE WIRE - SIGNALR协议的非正式描述

    原文 原文很简单,以下为机翻 WIRER ON THE WIRE - SIGNALR协议的非正式描述 我已经看到询问有关SignalR协议的描述的问题出现了很多.哎呀,当我开始关注SignalR时,我 ...

  9. php分享二十二:php面向对象

    1:static访问符 在类中使用static有两种主要用途.定义静态成员和定义静态方法.静态成员只保留一个变量的值,这个值对所有实例都是有效的 类的方法是static的,他所访问的属性也必须是sta ...

随机推荐

  1. C#LeetCode刷题之#840-矩阵中的幻方(Magic Squares In Grid)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3752 访问. 3 x 3 的幻方是一个填充有从 1 到 9 的不 ...

  2. JavaScript正则、错误处理、操作表单

    一.正则表达式:用单个字符串描述或者匹配符合特定语句规则的字符串 一些字符序列组合在一起,可以简单也可以复杂模式的,可以去搜索,可以去替换 二.语法: /表达式/修饰符(可选) var para=/i ...

  3. win10中搭建Linux子系统

    win10自带的Linux子系统,简称WSL(Windows Subsystem for Linux).优点是打通了Linux系统和windows系统,改变了传统虚拟机/双系统造成的两个系统相互隔绝的 ...

  4. 常用sql语句整理

    1.开/关 外键约束 -- 关 SET FOREIGN_KEY_CHECKS = 0; -- 开 SET FOREIGN_KEY_CHECKS = 1; 2.查看表的容量大小 use informat ...

  5. 第6篇 Scrum 冲刺博客

    1.站立会议 照骗 进度 成员 昨日完成任务 今日计划任务 遇到的困难 钟智锋 重构游戏逻辑代码 改写部分客户端代码,制作单机版 庄诗楷 进行了相关的装饰改进 与其他部分合成完成游戏 合成遇到bug, ...

  6. 更换git远程仓库地址

    通过命令直接修改远程仓库地址 git remote 查看所有远程仓库 git remote xxx 查看指定远程仓库地址 git remote set-url origin 你新的远程仓库地址 先删除 ...

  7. Java GUI 图书管理系统

    01 概述 一款功能强大的图书馆管理系统,功能齐全,小白/大学生项目实训,学习的不二之选. 02 技术 此系统使用 java awt 实现.java.awt是一个软件包,包含用于创建用户界面和绘制图形 ...

  8. java项目的心得,java项目的代码层次的架构划分

    java项目使用的架构是ssm(Spring+SpringMVC+MyBatis). 一.后台代码一般分三层,Controller,Service,Dao. 1.Controller层是对前端或者接口 ...

  9. 区块链入门到实战(6)之区块链 – 哈希(Hash)

    密码学中,最重要的函数之一是哈希函数.哈希函数将任意大小的数据(内容)映射到固定大小的数据(哈希值). 哈希函数是单向的,从内容生成哈希值很容易,但从哈希值映射到内容很难. 比特币使用SHA-256哈 ...

  10. C# DataTable查询示例

    代码 public void Test() { #region 初始化数据 /* 数据 张三 语文 34.00 张三 数学 58.00 张三 英语 61.00 李四 语文 45.00 李四 数学 87 ...