的运行机制及爬坑指南,虚假来电

JavaScript 中 this 的运行机制及爬坑指南

2018/03/14 · JavaScript
· this

原文出处: [Dr. Axel

javascript 函数中的 this 的四种绑定形式

2017/08/16 · JavaScript
· this

原文出处:
外婆的彭湖湾   

 javascript中的this和函数息息相关,所以今天,我就给大家详细地讲述一番:javascript函数中的this

一谈到this,很多让人晕晕乎乎的抽象概念就跑出来了,这里我就只说最核心的一点——函数中的this总指向调用它的对象,接下来的故事都将围绕这一点展开

 

(提醒前排的筒子们准备好茶水和西瓜,我要开始讲故事啦!!)

【故事】有一个年轻人叫“迪斯”(this),有一天,迪斯不小心穿越到一个叫
“伽瓦斯克利”(javascript)的 异世界,此时此刻迪斯身无分文,
他首先要做的事情就是——找到他的住宿的地方——调用函数的对象图片 1

虚假来电:HTML5振动API的恶意使用

2014/01/16 · HTML5 · 3
评论 ·
HTML5

本文由 伯乐在线 –
梧桐
翻译。未经许可,禁止转载!
英文出处:Terence
Eden。欢迎加入翻译组。

一个新的API出来了。HTML5
(很快)将支持用户设备振动。这明显是很有趣的事情,比如它可以用户触发提醒,提升游戏体验,以及其他各种好玩的事情,例如通过振动发送摩斯代码。

到目前为止,Chrome(以及其他Android浏览器)要使用位置信息、摄像头、地址簿等资源必须申请权限。这是一种安全措施防止你的个人信息在未授权的情况下泄露。

而现在使用HTML5振动API并不会在屏幕上触发警告。因为一般认为用这个功能几乎没有危害,毕竟在现实中它能干的坏事无非是持续消耗电量。事实就是这样简单吗?我不敢肯定。

Rauschmayer]()   译文出处:[众成翻译

woolll]()   

图片 2

在 JavaScript 中,this 这个特殊的变量是相对比较复杂的,因为 this
不仅仅用在面向对象环境中,在其他任何地方也是可用的。 本篇博文中会解释
this 是如何工作的以及使用中可能导致问题的地方,最后奉上最佳实践。

为了更好理解 this,将 this 使用的场景分成三类:

  • 在函数内部 this 一个额外的,通常是隐含的参数。
  • 在函数外部(顶级作用域中): 这指的是浏览器中的全局对象或者
    Node.js 中一个模块的输出。
  • 在传递给eval()的字符串中: eval() 或者获取 this
    当前值值,或者将其设置为全局对象,取决于 this
    是直接调用还是间接调用。

我们来看看每个类别。

this的默认绑定

 

【故事——线路1】如果迪斯(this)直到天黑前都没有找到能收留自己的住所,他眼看就要过上非洲难民的生活,
这时候,一位乐善好施的魔法师村长——window救世主一般地出现了:先住在我家吧!图片 3

【正文】

当一个函数没有明确的调用对象的时候,也就是单纯作为独立函数调用的时候,将对函数的this使用默认绑定:绑定到全局的window对象

JavaScript

function fire () { console.log(this === window) } fire(); // 输出true

1
2
3
4
function fire () {
     console.log(this === window)
}
fire(); // 输出true

上面的例子我相信对大多数人都很简单,但有的时候我们把例子变一下就会具有迷惑性:

JavaScript

function fire () { // 我是被定义在函数内部的函数哦! function
innerFire() { console.log(this === window) } innerFire(); //
独立函数调用 } fire(); // 输出true

1
2
3
4
5
6
7
8
function fire () {
  // 我是被定义在函数内部的函数哦!
     function innerFire() {
  console.log(this === window)
      }
     innerFire(); // 独立函数调用
}
fire(); // 输出true

函数 innerFire在一个外部函数fire里面声明且调用,那么它的this是指向谁呢?
仍然是window

许多人可能会顾虑于fire函数的作用域对innerFire的影响,但我们只要抓住我们的理论武器——没有明确的调用对象的时候,将对函数的this使用默认绑定:绑定到全局的window对象,便可得正确的答案了

下面这个加强版的例子也是同样的输出true

JavaScript

var obj = { fire: function () { function innerFire() { console.log(this
=== window) } innerFire(); // 独立函数调用 } } obj.fire(); //输出 true

1
2
3
4
5
6
7
8
9
var obj = {
   fire: function () {
       function innerFire() {
          console.log(this === window)
        }
        innerFire();   // 独立函数调用
     }
}
obj.fire(); //输出 true

注意】在这个例子中,
obj.fire()的调用实际上使用到了this的隐式绑定,这就是下面我要讲的内容,这个例子我接下来还会继续讲解

【总结】
凡事函数作为独立函数调用,无论它的位置在哪里,它的行为表现,都和直接在全局环境中调用无异

邪念

我们都看过那种无耻的广告做得跟Windows弹出窗一模一样,它们通常发出一个正当的系统请求:更新Java或类似的。

假如一个恶意网页弹出一个虚假的系统提示并同时振动,你有多大的信心能区分一个合法的弹出框和一个png图片?毕竟手机振动了,你就会认为它是真实的系统提示。

图片 4

(图1)

这时候你是收到了一个“空投”炸弹,还是说网页在跟你开个小玩笑?

页面广告自动播放声音本来就很烦人了。自动振动跟它比起来毫不逊色。回想一下你在满屏幕搜索那个推销保险的广告。

目前振动的强度还不能控制,只能控制持续时间。当然通过构造恶意代码去突破没打补丁的浏览器也不是不可能的,甚至可以让电机持续高负荷运转直到损坏。

this 在函数中

这是最常用的 this 使用方式,函数通过扮演三种不同的角色来表示 JavaScript
中的所有可调用结构体:

  • 普通函数(this 在非严格模式下为全局对象,在严格模式下为undefined)
  • 构造函数(this 指向新创建的实例)
  • 方法(this 是指方法调用的接收者)

在函数中,this 通常被认为是一个额外的,隐含的参数。

this的隐式绑定

【故事——线路2】
迪斯(this)穿越来异世界“伽瓦斯克利”(javascript)的时候,刚好身上带了一些钱,于是他找到一个旅馆住宿了下来

图片 5

当函数被一个对象“包含”的时候,我们称函数的this被隐式绑定到这个对象里面了,这时候,通过this可以直接访问所绑定的对象里面的其他属性,比如下面的a属性

JavaScript

var obj = { a: 1, fire: function () { console.log(this.a) } }
obj.fire(); // 输出1

1
2
3
4
5
6
7
var obj = {
     a: 1,
      fire: function () {
           console.log(this.a)
        }
}
obj.fire(); // 输出1

现在我们需要对平常司空见惯的的代码操作做一些更深的思考,首先,下面的这两段代码达到的效果是相同的:

JavaScript

// 我是第一段代码 function fire () { console.log(this.a) } var obj = {
a: 1, fire: fire } obj.fire(); // 输出1 // 我是第二段代码 var obj = { a:
1, fire: function () { console.log(this.a) } } obj.fire(); // 输出1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 我是第一段代码
function fire () {
      console.log(this.a)
}
  
var obj = {
      a: 1,
      fire: fire
  }
obj.fire(); // 输出1
// 我是第二段代码
var obj = {
        a: 1,
        fire: function () {
             console.log(this.a)
         }
}
obj.fire(); // 输出1

fire函数并不会因为它被定义在obj对象的内部和外部而有任何区别,也就是说在上述隐式绑定的两种形式下,fire通过this还是可以访问到obj内的a属性,这告诉我们:

1.  this是动态绑定的,或者说是在代码运行期绑定而不是在书写期

2.  函数于对象的独立性, this的传递丢失问题

(下面的描述可能带有个人的情感倾向而显得不太严谨,但这是因为我希望阅读者尽可能地理解我想表达的意思)

虚假来电

如果与HTML5
Audio一起使用,完全可以创建一个很真实的虚假”来电“,既有振动也有铃声。一旦”接听“,页面就可以播放一段音频:”喂,尽快回打给我,我的号码是“一个吸费号码”。接下来还可以使用URI自动打开拨号界面。

图片 6

(图2)

你能告诉我上面说的是真实的来电吗?如果你够仔细或许会发现。但如果页面正在播放你的默认铃声,然后设备还在振动,这时你就很可能迷糊。如果和WebRTC呼叫绑定,那实际上你看到的就是一个精心构造的骗局。

this 在普通函数中

在普通函数中,this
的值取决于模式:

  • 非严格模式: this
    是指向全局对象
    (在浏览器中为window对象)。
function sloppyFunc() { console.log(this === window); // true }
sloppyFunc();

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6bfd7bd4d414140440-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6bfd7bd4d414140440-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6bfd7bd4d414140440-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6bfd7bd4d414140440-4">
4
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6bfd7bd4d414140440-1" class="crayon-line">
function sloppyFunc() {
</div>
<div id="crayon-5b8f6bfd7bd4d414140440-2" class="crayon-line crayon-striped-line">
    console.log(this === window); // true
</div>
<div id="crayon-5b8f6bfd7bd4d414140440-3" class="crayon-line">
  }
</div>
<div id="crayon-5b8f6bfd7bd4d414140440-4" class="crayon-line crayon-striped-line">
sloppyFunc();
</div>
</div></td>
</tr>
</tbody>
</table>

  • 严格模式: this 的值为 undefined。
function strictFunc() { 'use strict'; console.log(this ===
undefined); // true } strictFunc();

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6bfd7bd58954780524-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6bfd7bd58954780524-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6bfd7bd58954780524-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6bfd7bd58954780524-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6bfd7bd58954780524-5">
5
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6bfd7bd58954780524-1" class="crayon-line">
function strictFunc() {
</div>
<div id="crayon-5b8f6bfd7bd58954780524-2" class="crayon-line crayon-striped-line">
   'use strict';
</div>
<div id="crayon-5b8f6bfd7bd58954780524-3" class="crayon-line">
   console.log(this === undefined); // true
</div>
<div id="crayon-5b8f6bfd7bd58954780524-4" class="crayon-line crayon-striped-line">
}
</div>
<div id="crayon-5b8f6bfd7bd58954780524-5" class="crayon-line">
strictFunc();
</div>
</div></td>
</tr>
</tbody>
</table>

也就是说,this 是一个设定了默认值(window或undefined)的隐式参数。
但是,可以通过 call() 或 apply() 进行函数调用,并明确指定this的值:“

function func(arg1, arg2) { console.log(this); // a console.log(arg1);
// b console.log(arg2); // c } func.call(‘a’, ‘b’, ‘c’); // (this, arg1,
arg2) func.apply(‘a’, [‘b’, ‘c’]); // (this, arrayWithArgs)

1
2
3
4
5
6
7
function func(arg1, arg2) {
        console.log(this); // a
        console.log(arg1); // b
        console.log(arg2); // c
    }
func.call(‘a’, ‘b’, ‘c’); // (this, arg1, arg2)
func.apply(‘a’, [‘b’, ‘c’]); // (this, arrayWithArgs)

隐式绑定下,作为对象属性的函数,对于对象来说是独立的

基于this动态绑定的特点,写在对象内部,作为对象属性的函数,对于这个对象来说是独立的。(函数并不被这个外部对象所“完全拥有”)

我想表达的意思是:在上文中,函数虽然被定义在对象的内部中,但它和“在对象外部声明函数,然后在对象内部通过属性名称的方式取得函数的引用”,这两种方式在性质上是等价的而不仅仅是效果上

定义在对象内部的函数只是“恰好可以被这个对象调用”而已,而不是“生来就是为这个对象所调用的”

 

借用下面的隐式绑定中的this传递丢失问题来说明:

JavaScript

var obj = { a: 1, // a是定义在对象obj中的属性 1 fire: function () {
console.log(this.a) } } var a = 2; // a是定义在全局环境中的变量 2 var
fireInGrobal = obj.fire; fireInGrobal(); // 输出 2

1
2
3
4
5
6
7
8
9
10
var obj = {
      a: 1,    // a是定义在对象obj中的属性   1
      fire: function () {
   console.log(this.a)
        }
      }
var a = 2;  // a是定义在全局环境中的变量    2
var fireInGrobal = obj.fire;  
fireInGrobal(); //  输出 2

上面这段简单代码的有趣之处在于: 这个于obj中的fire函数的引用(
fireInGrobal)在调用的时候,行为表现(输出)完全看不出来它就是在obj内部定义的
其原因在于:我们隐式绑定的this丢失了!!
从而 fireInGrobal调用的时候取得的this不是obj,而是window

上面的例子稍微变个形式就会变成一个可能困扰我们的bug:

JavaScript

var a = 2; var obj = { a: 1, // a是定义在对象obj中的属性 fire: function
() { console.log(this.a) } } function otherFire (fn) { fn(); }
otherFire(obj.fire); // 输出2

1
2
3
4
5
6
7
8
9
10
11
var a = 2;
var obj = {
    a: 1,    // a是定义在对象obj中的属性
    fire: function () {
          console.log(this.a)
     }
}  
function otherFire (fn) {
     fn();
}  
otherFire(obj.fire); // 输出2

在上面,我们的关键角色是otherFire函数,它接受一个函数引用作为参数,然后在内部直接调用,但它做的假设是参数fn仍然能够通过this去取得obj内部的a属性,但实际上,
this对obj的绑定早已经丢失了,所以输出的是全局的a的值(2),而不是obj内部的a的值(1)

视频演示

本文作者还录了一段视频,放在Youtube上了。

this 在构造函数中

如果通过new运算符调用函数,则函数将成为构造函数。
该运算符创建一个新的对象,并通过它通过this传递给构造函数:“

var savedThis; function Constr() { savedThis = this; } var inst = new
Constr(); console.log(savedThis === inst); // true

1
2
3
4
5
6
var savedThis;
function Constr() {
   savedThis = this;
}
var inst = new Constr();
console.log(savedThis === inst); // true

在JavaScript中实现,new运算符大致如下所示(更精确的实现稍微复杂一点):

function newOperator(Constr, arrayWithArgs) { var thisValue =
Object.create(Constr.prototype); Constr.apply(thisValue, arrayWithArgs);
return thisValue; }

1
2
3
4
5
function newOperator(Constr, arrayWithArgs) {
   var thisValue = Object.create(Constr.prototype);
   Constr.apply(thisValue, arrayWithArgs);
   return thisValue;
}

在一串对象属性链中,this绑定的是最内层的对象

在隐式绑定中,如果函数调用位置是在一串对象属性链中,this绑定的是最内层的对象。如下所示:

JavaScript

var obj = { a: 1, obj2: { a: 2, obj3: { a:3, getA: function () {
console.log(this.a) } } } } obj.obj2.obj3.getA(); // 输出3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obj = {
      a: 1,
      obj2: {
           a: 2,
           obj3: {
                a:3,
                getA: function () {
                    console.log(this.a)  
                 }
           }
       }
}
obj.obj2.obj3.getA();  // 输出3

源代码

下面是一个很基本的例子,你可以在手机上试验一下。(或点击这里看示例)

JavaScript

<body> <script type="text/javascript">
navigator.vibrate = navigator.vibrate || navigator.webkitVibrate ||
navigator.mozVibrate || navigator.msVibrate; navigator.vibrate([1000,
500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 1000,
500]); </script> <img width="100%"
src="phone.png"
onclick="window.location.href='tel:09098790815';"
/> <audio autoplay="autoplay"> <source
src="ring.mp3" /> </audio> </body>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;body&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
  navigator.vibrate = navigator.vibrate ||
       navigator.webkitVibrate ||
       navigator.mozVibrate ||
       navigator.msVibrate;
 
  navigator.vibrate([1000, 500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 1000, 500]);
&lt;/script&gt;
&lt;img width=&quot;100%&quot; src=&quot;phone.png&quot; onclick=&quot;window.location.href=&#039;tel:09098790815&#039;;&quot; /&gt;
&lt;audio autoplay=&quot;autoplay&quot;&gt;  
  &lt;source src=&quot;ring.mp3&quot; /&gt;  
&lt;/audio&gt;
&lt;/body&gt;

目前只有Android平台的Firefox支持,但毫无疑问其他浏览器将会跟进。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注