函数是对象,是引用类型(传址类型) 对象、类中的函数称为方法
声明定义
new Function(name, body)
不推荐
**_desc_**
对象创建函数_**params**_
{ string } name 函数名称_**params**_
{ string } body 函数体_**return**_
{ function }
var fn = new Function("add", "return 1 + 1");
var total = fn();
console.log(total); // 2
function name(...args) {}
* 全局变量,具名函数默认存在 window 中
* 可能导致 函数提升(可以在函数声明前,调用该函数)
**_desc_**
具名函数_**params**_
{ string } name 函数名称_**params**_
{ any } args 函数参数_**return**_
{ function }
var total = add(1, 2); // 此时产生了 函数提升
function add(a, b) {
return a + b;
}
console.log(total); // 3
const name = function(...args) {}
👍
* 使用 var 声明时,函数会挂载到最顶端作用域中,即 window
* 使用 let / const 声明时,函数不会挂载到 window 上
**_desc_**
匿名函数_**params**_
{ string } name 函数名称_**params**_
{ any } args 函数参数_**return**_
{ function }
// var total = add(1, 2); // error:add is not a function
const add = function(a, b) {
return a + b;
}
const total = add(1, 2);
console.log(total); // 3
参数类型
形式参数
函数声明时设置的参数,用于占位
* 行参数量 > 实参数量时,没有传入值的行参默认为 undefined
* 行参数量 < 实参数量时,多余的实参将被忽略且不会报错
const fn = function(a, b) {}; // a 和 b 为形式参数
实际参数
调用函数时实际传入的值
const fn = function(a, b) {}; // a 和 b 为形式参数
fn(1, 2); // 1 和 2 为实际参数
默认参数
形式参数的默认值
* es5 通过 || 实现默认值
* es6 通过行参赋值实现默认值。默认参数一般放在最后方。只占位不覆盖默认值可传入 undefined
|| 与 ?? 的区别
* || 当左侧值转换成布尔类型为 false 时,返回右侧的值
* ?? 当左侧值为 null 或 undefined 时,返回右侧的值
const fn1 = function(a, b) {
a = a || 100; // es5 默认值
return a + b;
};
const fn2 = function(a, b = 100) { // es6 默认值
return a + b;
};
不定参数
数量不确定的行参 使用 …args 表示不定参数,args 是传入的所有参数组成的数组
const fn = function(...args) {
console.log(args);
}
fn(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
arguments
函数内置的属性,效果等同于不定参数 是一个伪数组
const fn = function() {
console.log(arguments);
};
fn(1, 2, 3, 4, 5); // Arguments(5) [1, 2, 3, 4, 5]
函数类型
箭头函数
箭头函数是函数声明的简写方式 箭头函数是匿名函数
* 普通函数和箭头函数内部的 this 不同
* 普通函数内部拥有属于自己的 this。this 指向调用者,即谁调用指向谁。默认为 window,严格模式下为 undefined
* 构造函数是普通函数中的例外,其内部 this 指向实例对象
* 箭头函数内部没有属于自己的 this。 其内部的 this 实际上是从外部获取的,内部的 this 始终与上一层的 this 保持一致。箭头函数会一直向上寻找 this,直到找到最近的一个 this
* 普通函数内置有 arguments 属性。箭头函数没该内置属性
* 普通函数具有原型对象。箭头函数没有原型对象
* 普通函数可以作为构造函数,使用 new 关键字构造实例。箭头函数不能作为构造函数,使用 new 关键字时会抛出异常。
* 由于箭头函数没有原型对象,原型链上没有 constructor 构造方法,所以无法作为构造函数使用
/**
* this 指向说明
*/
const fn1 = function() {
console.log(this);
};
const arrowFn1 = () => {
console.log(this);
};
fn1(); // window,严格模式为 undefined
function Human(name) {
this.name = name;
this.eat = function() {
console.log(this); // 该函数是普通函数,内部 this 指向调用该方法的 this,即 Human 实例
},
this.run = function() {
fn1(); // 等同于 window.fn1()
},
this.sing = () => {
arrowFn1(); // 该函数是箭头函数,内部 this 与上一层函数内部的 this 保持一致。上一层也是箭头函数,则继续向上找,直到最顶层函数。顶层函数是 Human,为构造函数,构造函数 this 指向其实例,故该箭头函数的 this 也指向实例
},
}
var human = new Human("张三");
human.eat(); // { name: "张三" }
human.run(); // window,严格模式为 undefined
human.sing(); // { name: "张三" }
/**
* 普通函数内置有 arguments 属性。箭头函数没该内置属性
*/
const fn2 = function() {
console.log(arguments); // [1, 2, 3]
};
const arrowFn2 = () => {
console.log(arguments); // error: arguments is not defined
};
fn2(1, 2, 3);
arrowFn2(1, 2, 3);
/**
* 普通函数具有原型对象。箭头函数没有原型对象
*/
const fn3 = function() {};
const arrowFn3 = () => {};
console.log(fn3.prototype); // { constructor: f() }
console.log(arrowFn3.prototype); // undefined
/**
* 普通函数可以作为构造函数,使用 new 操作符构造实例。箭头函数不能作为构造函数,使用 new 操作符时会抛出异常
*/
const Fn4 = function() {};
const ArrowFn4 = () => {};
const fn4 = new Fn4();
console.log(fn4); // object
const arrowFn4 = new ArrowFn4(); // error: ArrowFn4 is not constructor
递归函数
递归指函数内部调用自身
* 主要用于数量不确定的循环操作
* 必须明确退出循环的条件,避免发生死循环
var width = 5;
function triangle(width) {
if (width <= 0) return;
console.log("*".repeat(width));
triangle(--width);
}
triangle(5);
*****
****
***
**
*
回调函数
A callback is a function that is passed as an argument to another function and is executed after its parent function has completed. 回调是一个函数,是另一个函数的参数,是在该函数完成后执行执行的
* 回调与同步、异步没有直接关系。但使用场景主要是异步回调
const getRunnerTime = function(callback) {
var count = 0;
var time = Date.now();
var interval = setInterval(() => {
count = Math.random();
if (count > 0.5) {
clearInterval(interval);
callback(Date.now() - time);
}
}, 500);
}
getRunnerTime(function(time) {
console.log(time);
});
立即执行函数
立刻执行,无需调用
**(function(...args) {})(...args)**
_**params**_
{ any } args 传入的参数_**return**_
{ void }
(function(a) {
console.log(a); // 100
})(100)
构造函数
使用 new 关键字调用的函数,称为构造函数 构造函数首字母一般大写
* 构造函数中的 this 指向其实例对象
* 构造函数的返回值始终是 this,人为修改返回值无效
* 构造函数不能使用箭头函数,箭头函数内部无法创建自己的 this
* 可以判断构造函数内部的 this 是否在其原型链上,实现不使用 new 关键字调用构造函数
/**
* 不使用 new 关键字实现调用构造函数
*/
function Male(name) {
this.name = name;
}
function Female(name) {
if (!(this instanceof Female)) {
return new Female(name);
}
this.name = name;
}
var male1 = new Male("zhangsan");
var male2 = Male("lisi");
var female1 = new Female("wangwu");
var female2 = Female("maliu");
console.log(male1); // { name: "zhangsan" }
console.log(male2); // undefined
console.log(window.name); // "lisi"
console.log(female1); // { name: "wangwu" }
console.log(female2); // { name: "maliu" }
标签函数
了解,很少见
函数解析模版字符串
* 调用标签函数时,不需要使用 (),直接方法名 + 模版字符串即可
* 标签函数本质是将模版字符串中的 ${} 符号作为分割符,将模版字符串分割为常量和变量数组后,再传入函数
**function name(constant, ...variable) {}**
_**params**_
{ string } name 函数名称_**params**_
{ array } constant 模版字符串中常量组成的数组_**params**_
{ array } variable 模版字符串中变量组成的数组_**return**_
{ any }
const fn = function(constant, ...variable) {
console.log(constant);
console.log(variable);
};
fn `Hello${1}World${2}`; // ["Hello", "World"], [1, 2]
this
this 指函数的上下文环境 this 是动态可变的 this 在函数声明时是无法确定的,只有在函数执行时才能确定函数的指向
* 普通函数内部拥有属于自己的 this。this 指向调用者,即谁调用指向谁。默认为 window,严格模式下为 undefined
* 构造函数是普通函数中的例外,其内部 this 指向实例对象
* 箭头函数内部没有属于自己的 this。 其内部的 this 实际上是从外部获取的,内部的 this 始终与上一层的 this 保持一致。
PS:代码示例见 函数类型 - 箭头函数**
动态设置 this
即对象方法借用
fn.apply(ctx, [...args])
* 立即执行
* 使用数组传参
* 本质:对象 ctx 借用 fn 方法
_**params**_
{ function } fn 被调用的方法_**params**_
{ object } ctx 借用方法的对象_**params**_
{ any } args 参数_**return**_
{ void }
function Human() {
this.run = function(a, b) {
console.log(`Human run~${a + b}`);
}
}
function fn () {};
var human = new Human();
human.run.apply(fn, [1, 2]); // "Human run~3"
fn.call(ctx, ...args)
* 立即执行
* 使用不定参数传参
* 与 apply 作用完全相同,唯一不同是传参格式不同
_**params**_
{ function } fn 被调用的方法_**params**_
{ object } ctx 借用方法的对象_**params**_
{ any } args 参数_**return**_
{ void }
function Human() {
this.run = function(a, b) {
console.log(`Human run~${a + b}`);
}
}
function fn () {};
var human = new Human();
human.run.call(fn, 1, 2); // "Human run~3"
fn.bind(ctx, ...args)
* 不会立即执行
* bind 的本质是复制函数行为,并返回新的函数
_**params**_
{ function } fn 被调用的方法_**params**_
{ object } ctx 借用方法的对象_**params**_
{ any } args 参数_**return**_
{ function }
function Human() {
this.run = function(a, b) {
console.log(`Human run~${a + b}`);
}
}
function fn () {};
var human = new Human();
var bindFn = human.run.bind(fn);
console.log(bindFn === fn); // false, bindFn 是复制出的具有 human.run() 方法体的新函数,并非原 fn 函数
bindFn(1, 2); // "Human run~3"
**bind 传参**
* bind 绑定时已传递全部参数,调用时再传参,此时传递的参数无效
* bind 绑定时不传递参数,调用时再传参,此时传递的参数有效
* bind 绑定时传递部分参数,调用时再传参,此时传递的参数会在绑定时传递的参数后面进行补位,多出的参数自动被删除
function sum(a, b, c) {
console.log(a + b + c);
}
function baseFn() {}
var fn1 = sum.bind(baseFn, 1, 2, 3);
var fn2 = sum.bind(baseFn);
var fn3 = sum.bind(baseFn, 1);
fn1(4, 5); // 6。绑定时已传递全部参数,此时参数无效
fn2(1, 2, 3); // 6。绑定时未传递参数,此时参数有效
fn3(4, 5, 6); // 10。绑定时传递部分参数,此时实参4和5有效,6已超出参数个数,自动删除
函数命名冲突
在同一个作用域中,相同名称的函数名会产出冲突(具名函数/var 会覆盖已有函数,let/const 会抛出异常) 解决思路:使相同名称的函数处于不同的局部作用域中
立即执行函数
(function(window) {
function add() {
console.log("fn1.add");
}
window.fn1 = {
add
}
})(window);
(function(window) {
function add() {
console.log("fn2.add");
}
window.fn2 = {
add
}
})(window);
fn1.add(); // "fn1.add"
fn2.add(); // "fn2.add"
块状作用域
{
function add() {
console.log("fn1.add");
}
window.fn1 = {
add
}
}
{
function add() {
console.log("fn2.add");
}
window.fn2 = {
add
}
}
fn1.add(); // "fn1.add"
fn2.add(); // "fn2.add"
Proxy 代理函数 ❓
* 在调用函数前添加一层拦截,可以对调用操作进行统一的处理。类似拦截器的作用
new Proxy(target, hander)
_**params**_
{ object } target 使用 Proxy 代理的目标对象_**params**_
{ object } hander 定义代理行为的对象_**return**_
{ object } 代理对象实例
function add(a, b) {
console.log(a + b);
}
const proxy = new Proxy(add, {
apply(fn, ctx, args) {
for (let key in args) {
args[key] = args[key] * 2;
}
fn.apply(ctx, args);
}
})
add(1, 2); // 3
proxy.apply(window, [1, 2]); // 6