第三节:函数相关面试题(函数基础、闭包、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.
(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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。