资讯专栏INFORMATION COLUMN

JavaScript高级程序设计——this

darkbug / 2984人阅读

摘要:另外注意,构造函数的函数名第一个字母大写规则约定。绑定函数被调用时,也接受预设的参数提供给原函数。一个绑定函数也能使用操作符创建对象这种行为就像把原函数当成构造器。

前言

本来this应该放在《上下文环境和作用域》中来讲,结果发现自己整理的时候,例子越来越多,所以多带带写一篇用于加深理解,以后工作或项目中发现一些this相关问题可以进一步完善文章的例子。

this 概念

在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了

因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。

构造函数

所谓构造函数就是用来new对象的函数。其实严格来说,所有的函数都可以new一个对象,但是有些函数的定义是为了new一个对象,而有些函数则不是。另外注意,构造函数的函数名第一个字母大写(规则约定)。例如:Object、Array、Function等。

function Foo() {
            this.name = "yzh";
            this.year = 1996;
            console.log(this); //Object { name: "yzh", year: 1998 }
        }
        var f1 = new Foo();
        console.log(f1.name); //yzh
        console.log(f1.year); //1996

以上代码中,如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象。

注意,以上仅限newFoo()的情况,即Foo函数作为构造函数的情况。如果直接调用Foo函数,而不是new Foo(),情况就大不一样了。

function Foo() {
            this.name = "yzh";
            this.year = 1996;
            console.log(this); //Object { name: "yzh", year: 1998 }
        }
        Foo();  //window

这种情况下this是window。

函数作为对象的一个属性

如果函数作为对象的一个属性时,并且作为对象的一个属性被调用时,函数中的this指向该对象。

var obj = {
            x: 10,
            fn: function() {
                console.log(this); //Object { x: 10, fn: fn() }
                console.log(this.x); //10
            }
        };
        obj.fn();

以上代码中,fn不仅作为一个对象的一个属性,而且的确是作为对象的一个属性被调用。结果this就是obj对象。

如果fn函数不作为obj的一个属性被调用,会是什么结果呢?

var obj = {
            x: 10,
            fn: function() {
                console.log(this); //window
                console.log(this.x); //undefined
            }
        };
        var f1 = obj.fn;
        f1();

如上代码,如果fn函数被赋值到了另一个变量中,并没有作为obj的一个属性被调用,那么this的值就是window,this.x为undefined。

函数用call或者apply调用

为了防止看不懂这块先理解下基础并重要的东西

函数内部属性

在函数内部,有两个特殊的对象:arguments和this.
主要介绍下arguments,它是类数组对象,包含传入函数的所有参数,这个对象还有一个叫callee的属性,该属性为一个指针,指向拥有这个arguments对象的函数。

这个例子主要用于消除函数的执行与同名函数的紧密耦合现象。代码如下:

function factorial(num) {
            if (num <= 1) {
                return 1;
            } else {
               // return num * factorial(num - 1);
                //1.一般的递归算法运用
                return num * arguments.callee(num - 1) 
                //2.更好的做法
            }
        }

        var trueFactorial = factorial;

        factorial = function() {
            return 0;
        };

        alert(trueFactorial(5)); //120
        alert(factorial(5)); //0
函数属性

每个函数都包含两个属性:length和prototype.
length表示函数希望接受的命名参数的个数

function sayName(name){
            alert(name);
        }      
        
        function sum(num1, num2){
            return num1 + num2;
        }
        
        function sayHi(){
            alert("hi");
        }
        
        alert(sayName.length);  //1
        alert(sum.length);      //2
        alert(sayHi.length);    //0

prototype属性这里不再重复介绍。

函数方法

每个函数都包含两个非继承而来的方法:apply()和call(),这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内的this对象值

apply()方法接受两个参数:一个是在其中运行函数的作用域,另一个是参数数组。

function sum(num1, num2){
            return num1 + num2;
        }
        
        function callSum1(num1, num2){
            return sum.apply(this, arguments);
        }
        
        function callSum2(num1, num2){
            return sum.apply(this, [num1, num2]);
        }
        
        alert(callSum1(10,10));   //20
        alert(callSum2(10,10));   //20

callsSum1()执行sum()函数时传入this作为this值(在全局作用域中调用,所以传入的对象为window对象)下面的例子同理。

call()方法与apply()方法差不多,区别在于接受参数的方式不同。简单来说就是:传递给函数的参数必须逐个列举出来。(如果是无参函数就不用传参数)

function sum(num1, num2){
            return num1 + num2;
        }
        
        function callSum(num1, num2){
            return sum.call(this, num1, num2);
        }
        
        alert(callSum(10,10));   //20

传递参数并非两个函数的真正用武之地,真正强大的地方是扩充函数赖以运行的作用域

window.color = "red";
        var o = { color: "blue" };
        
        function sayColor(){
            alert(this.color);
        }
        
        sayColor();            //red
        
        sayColor.call(this);   //red
        sayColor.call(window); //red
        sayColor.call(o);      //blue

如下代码如果不用函数的方法来实现。

window.color = "red";
        var o = { color: "blue" };
        
        function sayColor(){
            alert(this.color);
        }
        
        sayColor();     //red
        
        o.sayColor = sayColor;
        o.sayColor();   //blue

如果要输出o对象里的color属性值,必须把sayColor函数赋给对象o并调用 o.sayColor()时,this引用的对象为o

回归要讲的例子

当一个函数被call和apply调用时,this的值就取传入的对象的值。

var obj = {
            x: 10
        };
        var fn = function() {
            console.log(this); //Object { x: 10 }
            console.log(this.x); //10
        };
        fn.call(obj);
全局 & 调用普通函数

在全局环境下,this永远是window,这个应该没有非议。

console.log(this === window); //true

普通函数在调用时,其中的this也都是window。

var x = 10;
        var fn = function() {
            console.log(this); //window
            console.log(this.x); //10
        }
        fn();

注意以下的情况

var obj = {
            x: 10,
            fn: function() {
                function f() {
                    console.log(this); //Window 
                    console.log(this.x); //undefined
                }
                f();
            }
        };
        obj.fn();

函数f虽然是在obj.fn内部定义的,但是它仍然是一个普通的函数,this仍然指向window。

总结:this指向调用该函数的对象

构造函数的prototype中,this代表着什么
function Fn() {
            this.name = "yzh";
            this.year = 1996;
        }
        Fn.prototype.getName = function() {
            console.log(this.name);
        }
        var f1 = new Fn();
        f1.getName(); //yzh

在Fn.prototype.getName函数中,this指向的是f1对象。因此可以通过this.name获取f1.name的值

bind()

bind()方法创建一个新的函数, 当被调用时,它的this关键字被设置为提供的值 ,在调用新函数时,提供任何一个给定的参数序列。

这个函数其实也很重要,只是当时红宝书也没提及很多。

语法

fun.bind(thisArg[, arg1[, arg2[, ...]]])

参数

thisArg

当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。

arg1, arg2, ...

当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
返回值

返回由指定的this值和初始化参数改造的原函数拷贝

描述

bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

示例

创建绑定函数

this.x = 9; 
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 返回 81

var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域

// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81
End

欢迎拍砖,有更多的好的例子,欢迎提出,文中有误的地方也欢迎指正。

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

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

相关文章

  • javascript高级程序设计》笔记:继承

    摘要:继承和前面两篇文章中的知识非常相关,如果对函数创建原理和原型链不熟悉,请猛戳高级程序设计笔记创建对象高级程序设计笔记原型图解继承,通俗的说,就是将自身不存在的属性或方法,通过某种方式为自己所用文章分别介绍原型链继承继承借用构造函数继承组合继 继承和前面两篇文章中的知识非常相关,如果对函数创建原理和原型链不熟悉,请猛戳:《javascript高级程序设计》笔记:创建对象《javascri...

    JerryC 评论0 收藏0
  • 《你不知道的javascript》笔记_对象&原型

    摘要:上一篇你不知道的笔记写在前面这是年第一篇博客,回顾去年年初列的学习清单,发现仅有部分完成了。当然,这并不影响年是向上的一年在新的城市稳定连续坚持健身三个月早睡早起游戏时间大大缩减,学会生活。 上一篇:《你不知道的javascript》笔记_this 写在前面 这是2019年第一篇博客,回顾去年年初列的学习清单,发现仅有部分完成了。当然,这并不影响2018年是向上的一年:在新的城市稳定、...

    seasonley 评论0 收藏0
  • JavaScript 高级技巧

    摘要:如果直接调用构造函数,那么会指向全局对象然后你的代码就会覆盖的原生。冻结对象最严格的防篡改就是冻结对象。中央定时器动画地址参考书籍高级程序设计忍者秘籍 1 安全类型检测 javascript内置类型检测并不可靠 safari某些版本( var person = {name: wdd}; undefined > Object.preventExtensions(person); Obj...

    wean 评论0 收藏0
  • javascript高级程序设计》之事件

    摘要:特性这样指定事件处理程序具有一些独到之处。级事件处理程序通过获得要操作对象的引用,用把一个函数赋值给一个事件处理程序属性,比如。以这种方式添加的事件处理程序会在事件流的冒泡阶段被捕获。级事件处理程序级事件定义了两个方法,。 一、事件处理程序 响应某个事件的函数叫做事件处理程序(或事件侦听器),为事件指定处理程序的方式有下面几种。 1、html特性 这样指定事件处理程序具有一些独到之处...

    Render 评论0 收藏0

发表评论

0条评论

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