第三节:函数相关面试题(函数基础、闭包、this指向等)

一. 函数基础

1. 函数由哪几种构造方式,其中函数声明和函数表达式的区别?

(1). 函数声明

(2). 函数表达式

(3). Function函数(了解即可)

//除了最后一个参数是函数体外,其余的都是形参
var sum = new Function('num1','num2', 'return a+b ')

其中函数声明和函数表达式的区别:函数声明的写法存在函数提升,函数表达式不存在

{
	console.log("1. 函数声明和函数表达式的区别");
	console.log(sum1(1, 2)); //3
	// console.log(sum2(1, 2));   //ReferenceError: Cannot access 'sum2' before initialization

	// 函数声明
	function sum1(a1, a2) {
		return a1 + a2;
	}
	// 函数表达式
	const sum2 = function (a1, a2) {
		return a1 + a2;
	};
}

2. 形参和实参的区别?

  函数声明的值为形参,传递进来的值为实参

 

3. 普通函数和构造函数的区别?

创建对象的时候,经常使用构造函数

(1). 构造函数首字母大写。

(2). 构造函数内部有this关键字

(3). 使用构造函数的时候,要用new关键字创建。

 

4. apply( )函数,call()函数,bind( )函数的使用与区别

(1) 共同的作用

    A. 改变被调用被调用函数的中的this指向,第一个参数都是传递this指向位置,如果传递null 或 undefined,那么函数中this指向全局

    B. 都可以接收被调用函数的参数,最终进行调用

(2) 区别

   A. apply数组形式传递参数,call 和 bind都是以参数列表传递参数。

   B. apply和call都是函数立即调用,   bind返回的是一个新函数,需要手动调用一下。

{
	console.log("2. apply call bind 基本用法");
	function sum(a, b) {
		return a + b;
	}
	// 调用
	console.log(sum.apply(null, [1, 2]));
	console.log(sum.call(null, 1, 2));
	console.log(sum.bind(null, 1, 2)());
}

(3) 应用场景

A. 求数组的最大值和最小值

分析:Math.max 函数,默认接收的参数为列表的形式,借助apply调用,可以传递函数的形式。

{
	console.log("3. apply call bind 应用场景-----数组求最大值、最小值");
	let array1 = [1, 3, 4, -9, 100];
	console.log(`最大值:${Math.max.apply(null, array1)}`);
	console.log(`最大值:${Math.min.apply(null, array1)}`);
}

B. 将arguments转换成数组

{
	console.log("3. apply call bind 应用场景-----将arguments转换成数组");
	function fn() {
		return Array.prototype.slice.call(arguments);
	}
	console.log(fn(1, 3));
}

C. 实现继承(下一节详细分析)

{
	console.log("3. apply call bind 应用场景-----实现继承");
	function Person(name, age) {
		this.name = name;
		this.age = age;
	}
	function Student(name, age, gender) {
		Person.call(this, name, age);
		this.gender = gender;
	}
	let student = new Student("ypf", 19, "男");
}

 

5. 手写代码[未完]

 

 

 

 

二. 闭包问题

1. 什么是闭包?【重点】

闭包就是能读取其它函数内部变量的函数,而在js内,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单的理解为:定义在一个函数内部的函数

2. 闭包的优缺点

优点:

 (1). 保护函数内部变量的安全,用于封装,防止其它变量的导入后,造成命名冲突。

 (2). 适当的时候,可以在内存中维护变量并缓存,提高执行效率

缺点:

  消耗内存,闭包引用的外部函数对象是无法销毁的,所以闭包比一般的函数更废内存。

  (通常:函数的活动对象会随着执行上下文环境一起被销毁)

3. 闭包的应用

  在没有模块化概念之前,使用立即函数的模式(实际就是闭包),来实现模块化的封装。

{
	console.log("4. 闭包的应用---没有模块化前的立即函数封装");
	var stack = (function () {
		//使用数组模拟栈
		var arr = [];
		return {
			push: function (value) {
				arr.push(value);
			},
			pop: function () {
				return arr.pop();
			},
			size: function () {
				return arr.length;
			},
		};
	})();
	stack.push("abc");
	stack.push("def");
	console.log(stack.size()); // 2
	console.log(stack.pop()); // def
	console.log(stack.size()); // 1
}

4. 闭包面试题分析

(1). 使用闭包解决for循环绑定事件的时候,只有最后一个绑定成功  (实际最简单的办法是 var 改为 let

  var list = document.getElementsByTagName("ul")[0].children;
      for (var i = 0; i < list.length; i++) {
        (function (index) {
          list[index].onclick = function () {
            console.log(index);
          };
        })(i);
      }

(2). 试题2

{
	console.log("4. 闭包的面试题分析");
	var userName = "zhangsan";
	var person = {
		userName: "lisi",
		method: function () {
			return function () {
				return this.userName;
			};
		},
	};
	console.log(person.method()()); //zhangsan  (需要在浏览器下运行)
}

(3). 试题3

{
	console.log("4. 闭包的面试题分析3");
	function create() {
		var a = 100;
		return function () {
			console.log(a);
		};
	}
	var fn = create();
	var a = 200;
	fn(); // 100
}

(4). 试题4

{
	console.log("4. 闭包的面试题分析4");
	function print(fn) {
		var a = 200;
		fn();
	}
	var a = 100;
	function fn() {
		console.log(a);
	}
	print(fn); // 100
}

 

三. this指向问题

(详见:https://www.cnblogs.com/yaopengfei/p/15880283.html)

1. 再次总结基本规则

(1).  默认绑定,this指向window

(2).  隐式绑定,xx.fn(),this指向xx

(3). 显式绑定,apply、call、bind,this指向传递进去的对象。

(4). new绑定,this指向new出来的实例

优先级问题:new绑定 > 显示绑定(apply/call/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)

2. 面试题分析

(1). 试题1

        // 分析:默认绑定+隐式绑定
        {
            console.log("--------试题1---------");
            var a = 10;
            var obj = {
                a: 120,
                method: function () {
                    var bar = function () {
                        console.log(this.a); // 10
                    };
                    bar(); //这里属于默认绑定,this指向windows
                    return this.a;
                },
            };
            console.log(obj.method()); // 输出结果: 10   120  
        }

(2). 试题2

        // 分析:new绑定,this指向person实例
        {
            console.log("--------试题2---------");
            var num = 10;
            function Person() {
                //给全局变量重新赋值
                num = 20;
                // 实例变量
                this.num = 30;
            }
            Person.prototype.getNum = function () {
                return this.num;
            };
            var person = new Person();
            console.log(person.getNum()); // 输出结果30 
        }

(3). 试题3

       // 分析:默认绑定+隐藏绑定
        {
            console.log("--------试题3---------");
            function fn() {
                console.log(this);
            }
            let obj = {
                fn: fn,
            };
            fn(); //window
            obj.fn(); //obj
        }

 (4). 试题4

        // 分析:隐式绑定 + 默认绑定
        {
            console.log("--------试题4---------");
            var fullName = "language";
            var obj = {
                fullName: "javascript",
                prop: {
                    getFullName: function () {
                        return this.fullName;
                    },
                },
            };
            console.log(obj.prop.getFullName()); // undefined   【隐式绑定,this指向obj.prop】
            var test = obj.prop.getFullName;
            console.log(test());   // language   【默认绑定,this执行windows】
        }

(5). 试题5

        // 分析:这里由于前面没有添加this,也就是没有写成this.val,所以这里的val指向了全局变量
        {
            console.log("--------试题5.1 ---------");
            var val = 1;
            var json = {
                val: 10,
                dbl: function () {
                    val *= 2; //这里由于前面没有添加this,也就是没有写成this.val,所以这里的val指向了全局变量
                    // 等价
                    // val = val * 2;
                },
            };
            json.dbl();
            console.log(json.val + val); // 12  【10+2=12】
        }
        // 分析: 隐式绑定,this.val中this指向json
        {
            console.log("--------试题5.2 ---------");
            var val = 1;
            var json = {
                val: 10,
                dbl: function () {
                    this.val *= 2; //
                },
            };
            json.dbl();
            console.log(json.val + val); // 21  【20+1=21】
        }

 (6). 试题6

        // 分析:隐式绑定+显式绑定
        {
            console.log("--------试题6---------");
            //全局变量
            var value = 10;
            var obj = {
                value: 20,
            };
            // 全局函数
            var method = function () {
                console.log(this.value);
            };
            method(); // 10    
            method.call(obj); // 20
            method.apply(obj); // 20
            var newMethod = method.bind(obj);
            newMethod(); // 20
        }

 

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
posted @ 2022-07-20 15:05  Yaopengfei  阅读(142)  评论(2编辑  收藏  举报