JS引用类型 --- 函数(含this指向面试题)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS引用类型 --- 函数(含this指向面试题)相关的知识,希望对你有一定的参考价值。

一、函数基础

1. 函数:可重用的代码块

2. 函数可以作为参数、返回值使用

3. 函数实际上是 Function 的实例,其数据类型是Object

4. 但typeof Function 值为 function; FunctionObjectArray 等都是构造函数

5. 函数没有重载,函数名重复后会被覆盖

  • 重载:其他语言,可以为一个函数编写两个定义,只要这两个定义的签名(接受参数的类型和数量)不同即可

  • 深入理解函数 为什么没有重载:函数是引用类型的,函数名可理解为函数的指针,函数名重名,即为函数重新定义指针的指向。

6. 模拟实现 JS函数的重载

  • 根据arguments对象的length值进行判断,执行不同的代码 参考
<script>
    function overLoading() {   
        // 根据arguments.length,对不同的值进行不同的操作
          
        switch (arguments.length) {    
            case 0:
                /*操作1的代码写在这里*/       
                break;    
            case 1:
                /*操作2的代码写在这里*/       
                break;    
            case 2:
                /*操作3的代码写在这里*/          
                //后面还有很多的case......
        }
    }
</script>

二、函数 创建 的3种方式

1. 函数声明 方式 function

  • 预解析是将整个函数体提前提前
<script>
    fn();   // 可以访问到

    function fn() {
        
    }

    console.log(fn); //输出整个函数体, 即:function fn() {}
</script>

2. 函数表达式 方式 var fn =

  • 预解析是将var fn; 提前,并没有将函数体提前
<script>
    fn();   // 不能访问到,报错
    var fn = function () {
        
    }

    console.log(fn); //输出整个函数体, 即:function fn() {}
</script>

3. 构造函数 方式 new Function

  • new Function(‘新函数参数‘,‘新函数参数‘,....,‘函数体‘)

  • 注意:
    • 参数都是字符串
    • 最后一个字符串参数是 新函数体;前面所有参数都是 新函数的 参数;
<script>
    var fn = new Function(‘a‘, ‘b‘, ‘c‘, ‘return a+b+c‘);

    console.log(fn(1, 2, 3));
</script>
  • 作用:将字符串,转为可执行的代码
    • 模板引擎原理 new Function()
    • 能够将json 对象形式的字符串 转为 json 对象,代码如下:
<script>
    var str = ‘{name: "zhangxin", age: 18}‘;
    var aa = new Function(‘return‘ +  str);

    console.log(aa());
</script>

4. 函数在哪个作用域内创建的,就会在哪个作用域内执行;与函数调用的环境无关

5. 函数一创建,就有了 prototype 属性,指向原型对象(Math函数除外)

6. 匿名函数 的表现形式

function () {};
var f = function () {};

三、函数的 参数

1. 形参

  • 形参: 函数定义的参数

  • 调用函数时,实参个数 不等于 形参个数时,不会报错;因为,在函数体内有arguments来存储这些实参

2. 实参

  • 实参:由arguments 统一管理,arguments 是一个伪数组,只有.length属性

3. 参数可看做 参数函数内部的局部变量;且参数的 声明和赋值 被提升到函数最顶端

  • 传实参 就是 给局部变量初始化

  • 不传实参 则参数值为undefined

4. 关于 函数形参 与 函数内变量 重名问题分析?

  • 参数 可以看做是函数内部的局部变量

    • 如果 形参 与 函数内 的变量名冲突,则后者会覆盖前者

    • 如果 形参 与 函数内 的函数名(函数声明方式创建的函数)冲突,则形参变为函数

<script>
    function fn(m) {
        console.log(m);
        m = 2;
        console.log(m);
    }

    fn(10);
</script>
// 10 2
<script>
    function fn(m) {
        console.log(m);
        function m() {};
        console.log(m);
    }

    fn(10);
</script>
// 函数体 函数体
<script>
    function fn(m) {
        console.log(m);
        m = function () {};
        console.log(m);
    }

    fn(10);
</script>
// 10 函数体

四、函数的 返回值

  • 函数的返回值:可有可无,不会报错

  • 没有返回值、没有明确的返回值,输出函数的调用,结果为 undefined

<script>
    function fn1() {
        console.log("aaa");
        return;
    }
    console.log(fn1());  //aaa  undefined
</script>
  • 函数可以作为返回值:被返回

五、函数的 直接属性和方法

1. 属性:length,获取形参的个数

2. 属性:prototype,指向原型对象,保存函数的所有实例方法

3. 属性:name, 获取当前的函数名(IE不支持)

4. 属性:caller,(被废弃)可以获得当前函数在哪个函数内部被调用,全局调用,结果为null

5. 方法借用:apply()(非继承来的方法)

6. 方法借用:call()(非继承来的方法)

  • 方法借用 并自动调用; 改变this指向

  • apply()call() 借用方法后立即被自动调用返回值是:借用方法的返回值

  • 借用方法:apply()call()
    • 第1个参数:this指向的对象,可不传;(不传、传null、传undefined时,this都指向window);

    • 第2个参数:借用方法的参数

    • 返回值是:借用方法的返回值

  • apply()call() 区别 在于方法内的第二个参数
    • 方法.apply() : 第2个参数:数组,数组内每一项是借用方法的参数

    • 方法.call() :第2个、第3个等参数:是单独的项,是借用方法的参数

  • apply() 借用 Math.max 求数组中的最大值

Math.max.apply(null, [14, 3, 77])
<script>
    var arr = [1, 2, 3, 4];

    var result1 = arr.toString(); // 1,2,3,4 使用了数组实例自己的toString()方法
    var result2 = Object.prototype.toString.call(arr); // 借用了对象的toString()方法
    var result3 = Object.prototype.toString.apply([arr]);
    console.log(result1);  // 1,2,3,4
    console.log(result2);  // [object Array]
    console.log(result3);  // [object Array]
</script>
<script>
    function fn () {
        console.log(this);
    }

    fn.call();              // window
    fn.call(null);          // window
    fn.call(undefined);     // window
</script>

5. 方法借用 bind() (ES5定义的)

  • 方法借用 需手动调用; 改变this指向

  • 返回值:借用的方法(需要手动调用,这就是和 apply call 的区别)

  • 用法:方法.bind(this指向的对象); (不传、传null、传undefined时, this指向window)

<script>
    var bar = function(a, b) {
        console.log(this.x);
        console.log(a);
        console.log(b);
    }
    var foo = {
        x: 3
    }

    bar.bind(foo); // 无输出,因为 借用了方法,还没被调用
    bar.bind(foo)(1, 2);    // 3 1 2
</script>
<script>
    var length = 20;

    var fn = function () {
        console.log(this.length);
    };

    var obj = {
        length: 10
    };

    fn.bind()();       // 20
    fn.bind(obj)();    // 10
</script>
<script>
    const length = 20;

    var fn = function () {
        console.log(this.length);
    };

    var obj = {
        length: 10
    };

    fn.bind()();       // 0, const 声明的变量在块级作用域中,不在window中,window中length属性值为0
</script>

六、函数的 内部属性(成员)

1. arguments对象;保存实参;伪数组

  • 每一个函数体内,都有一个 arguments;只能在函数体内访问

  • 伪数组,只有.length属性;转为真数组: [].slice.apply(arguments)

  • 是一个对象,属性:arguments.length; 取值:arguments[0]

2. arguments.callee 指向当前函数:解耦实现函数的递归

  • arguments.callee 指向一个正在执行的函数的指针
<script>
    function aaa() {
        console.log(arguments.callee);  // 整个函数体
        return arguments.callee();  // 实现函数的递归(解耦)
    }

    aaa();
</script>

3. this对象:

this具有动态性,【谁调用函数,函数内部的this指向谁】 ; 【执行函数时,要回到创建函数的环境下执行】
(1) 函数调用模式: this指向 window
特殊:setTimeout / setInterval 等回调函数中, this 的指向:window,因为回调函数是window调用的
(2) 方法调用模式: this指向 调用方法的对象
特殊:DOM 绑定事件,事件回调函数中 this 指向 DOM
  • 涉及arr[0] = 函数,实际上函数作为 arr对象中的方法被调用

  • 实际上:this指向数组对象,解释:【数组是对象,arr[0] ===> 数组对象.方法】

(3) 构造函数调用模式: this指向 新创建的对象
(4) 方法借用模式: this指向,参数对象 / 不传参或者穿 null undefined 的话this 指向 window
this面试题
  • 第1题,输出什么
<script>
    var length = 10;
    function fn() {
        console.log(this.length);
    }
    var obj = {
        length: 3,
        method: function (fn) {
            (false || arguments[0])();
        }
    };

    obj.method(fn, 123, 14, 12, 4);
</script>
  • 第2题,输出什么
<script>
    var arr = [];
    arr[0] = function () {
        console.log(this.length);
    }

    arr[1] = 123;
    arr[2] = 22;
    arr[0]();
</script>
  • 第3题,输出什么
<script>
    var age = 38;
    var obj = {
        age: 18,
        getAge: function () {
            function fn() {
                console.log(this.age);
            }

            fn();
        }
    };

    obj.getAge();
</script>
  • 第4题,输出什么
<script>
    var obj ={
        say: function () {
            console.log(this);
        }
    };

    obj.say();
    (obj.say)();
    (obj.say = obj.say)();
    (false || obj.say)();
</script>
  • 第5题,改变this指向,让其指向 obj
<script>
    function fn() {
        console.log(this);
    }

    var obj = {};

    // 方法1:
    obj.fn = fn;
    obj.fn();

    // 方法2:
    fn.apply(obj);

    // 方法3:
    fn.call(obj);

    // 方法4:
    fn.bind(obj)();
</script>
  • 第6题,判断this指向
<script>
    var obj = {
        fn: function () {
            console.log(this);
        }
    };

    setTimeout(obj.fn, 1000);
</script>
  • 第7题,使用setTimeout调用函数,并使 this 指向 obj
<script>
    var obj = {
        fn: function () {
            console.log(this);
        }
    };

    // 方法1:
    setTimeout(obj.fn.bind(obj), 1000);
    setTimeout(obj.fn.apply(obj), 1000);
    setTimeout(obj.fn.call(obj), 1000);

    // 方法2:
    setTimeout(function () {
        obj.fn();
    }, 1000);
</script>
  • 第8题,输出什么 【重要】
<script>
    var length = 10;
    function fn() {
        console.log(this.length);
    }
    var obj = {
        length: 5,
        method: function (f) {
            f();
            arguments[0]();
            arguments[0].call(this);
            arguments[0].call();
        }
    };

    var obj2 = {
        method: (function () {
            console.log(this);
        })()
    };

    obj.method(fn);
</script>
  • 第9题,输出什么 【重要】
<script>
    var name = ‘windowName‘;

    var obj = {
        name: ‘objName‘,
        getName: function() {
                return this.name;

        }
    };

    console.log(obj.getName());
    console.log((obj.getName)());
    console.log((obj.getName = obj.getName)()); // 先赋值,再执行赋值后的结果;因为赋值表达式的值是本身,所以this不能得到维持
</script>
  • 第10题,修改obj中的代码,使输出结果为 windowName
<script>
    var name = ‘windowName‘;

    var obj = {
        name: ‘objName‘,
        getName: function() {
                return this.name;

        }
    };

    console.log(obj.getName());
</script>
  • 第11题,输出什么
<script>
    var name = ‘windowName‘;

    var obj = {
        name: ‘objName‘,
        getName: function() {
            return function() {
                return this.name;
            }
        }
    };

    console.log(obj.getName()());
</script>
  • 第12题:修改obj中代码,是输出结果为 objName
<script>
    var name = ‘windowName‘;

    var obj = {
        name: ‘objName‘,
        getName: function() {
            return function() {
                return this.name;
            }
        }
    };

    console.log(obj.getName()());
</script>
this面试题答案
第1题: 10
第2题: 3
第3题: 38
第4题: obj  obj  window  window
第6题: window
第8题: window  10  1  5  10
第9题: objName  objName  windowName

第10题:
<script>
    var name = ‘windowName‘;

    var obj = {
        name: ‘objName‘,
        getName: function() {
            
            return (function () {
                return this.name
            })()
        }
    };

    console.log(obj.getName()); // objName
</script>

第11题:windowName

第12题:
<script>
    var name = ‘windowName‘;

    var obj = {
        name: ‘objName‘,
        getName: function() {
            var that = this;
            return function() {
                return that.name;
            }
        }
    }

    console.log(obj.getName()());
</script>

七、JS中 将字符串转为 可执行的代码 eval()Function()

eval( ) 将字符串转为可执行的代码、和Function相同功能,区别在哪?

  • Function 和 eval 的区别和联系:

    • 相同点:都能够将字符串转为js 代码来执行

    • 不同点:eval效率相对高、但全局污染比较严重,Function 反之

  • 权衡利弊

    • 考虑效率使用eval

    • 考虑安全性用Function

    • eval 会造成 全局污染

<script>
    // 将JSON字符串转化为js对象
    var jsonStr = ‘var obj = {"name": "小明", "age": 19}‘;
    
    eval(jsonStr);

    console.log(obj);
</script>

以上是关于JS引用类型 --- 函数(含this指向面试题)的主要内容,如果未能解决你的问题,请参考以下文章

JS面试题(进阶)——原型链、this指向、闭包

前端面试题总结二(js原型继承)

js -this,newcallapplybind 相关面试题

js面试题1

《2w字大章 38道面试题》彻底理清JS中this指向问题 #yyds干货盘点#

《2w字大章 38道面试题》彻底理清JS中this指向问题