一、函数基础
1. 函数:可重用的代码块
2. 函数可以作为参数、返回值使用
3. 函数实际上是 Function
的实例,其数据类型是Object
4. 但typeof Function
值为 function
; Function
、Object
、Array
等都是构造函数
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>