分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 教程案例

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

发布时间:2023-09-06 01:32责任编辑:彭小芳关键词:面试题

一、函数基础

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指向面试题)

原文地址:http://www.cnblogs.com/zxvictory/p/8094424.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved