前言
-
本篇博客因篇幅有限,仅提取出个人认为十分重要且有用的精华,由于本人能力有限,文章可能未能一一提及,仅供各位同学参考学习。
-
本文章所有代码已在WebMaker上有代码测试用例。请点击略解《JavaScript语言精粹》代码片段访问配合学习。
函数对象
JavaScript的函数就是对象。每个函数在创建的时候会附加两个隐藏属性:函数的上下文和实现函数行为的代码。
附属资料:理解JavaScript函数是一等公民
函数字面量
函数可以通过函数字面量来创建。
let add = function(a,b) {
return a + b
}
调用方式
方法调用模式
当一个函数被保存为对象的一个属性时候,我们称他为方法。
let http = {
url: '',
get: ()=>{
console.log("get方法")
}
}
方法可以使用this访问自己所属的对象。
函数调用模式
当一个函数并非一个对象的属性时,那么它就是被当作一个函数来调用的。
let sum = add(3,4)
this指向的是全局对象。这是一个语言设计上的错误,正确的设计应该是当内部函数调用的时候this应该仍然绑定到外部函数的this变量而不是全局对象。
因此我们在ES6之前,大多数都是使用let that = this去访问外部函数的this。
var state = {
value: 1,
add: function() {
let that = this
const addNum = function (){
that.value++
}
addNum()
}
}
state.add()
console.log(state.value)
ES6之后,有了箭头函数,现箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者。
const state = {
value: 1,
add: function() {
const addNum = ()=>{
this.value++
}
addNum()
}
}
state.add()
console.log(state.value)
构造器调用模式
- 使用 new 引导构造函数, 创建了一个实例对象
- 在创建对象的同时, 将this指向这个刚刚创建的对象
- 在构造函数中, 不需要 return , 会默认的 return this
- return 非对象全部失效 return 其他对象, 返回 return 后面的对象
不推荐使用这种形式的构造器函数,如果调用构造器函数时没有在前面加上new,回发生错误!
var Status = function (status) {
this.status = status
}
Status.prototype.get_status = function () {
return this.status
}
let myStatus = new Status('200')
console.log(myStatus.get_status())
Apply调用模式
函数可以拥有方法,apply方法让我们构建一个参数数组传递给调用函数,它也允许使用this的值,apply接受两个参数,第一个是绑定this的值,第二个是参数数组。
// 修改this指向
let state = {
name: 'xiaoxi'
}
let fun = function() {
console.log(this.name)
}
fun.apply(state)
// 传递参数
let state2 = {
name: 'xiaoxi'
}
let fun2 = function(str,str2) {
console.log(str + this.name + str2)
}
fun2.apply(state,['我是',',收到请回复'])
除apply之外还有call、bind能够实现改变this对象。
- bind 返回的是一个新的函数,你必须调用它才会被执行。
- bind和call 的参数是直接放进去的参数用逗号分开。
- apply 的所有参数都必须放在一个数组里面传进去。
- call 和 apply 都是立即执行。
JavaScript 中 call()、apply()、bind() 的用法
参数
函数被调用的时候会得到一个arguments数组,函数可以通过访问此数组得到所有调用它传递的参数列表,包括没有分配的、未定义的参数。
因为语言的设计错误,arguments并非是一个真正的数组,而是类数组,只有length长度属性,没有数组的方法。
闭包
闭包 我们在bing搜索上会得到一句话:能够读取其他函数内部变量的函数。相信很多同学(包括我)应该马上就会理解并跳过,但是我们其实可以深入了解一下为什么要叫闭包?对谁封闭?什么是包?
作用域可以使内部函数访问到外部函数的变量,闭包则是一种有趣的场景,内部函数的生命周期比外部函数的生命周期长,通过调用一个函数的初始化,该函数返回一个对象字面量,并且在内部定义了一个变量,该变量对对象字面量里面的方法可见,但函数的作用域对其他地方的程序是不可见的。
结论:函数的内部变量对其他地方的程序封闭,包指的是函数返回的对象字面量,且对象字面量中的方法对函数的内部变量可见,总称闭包。
const fun = function() {
// 函数内部变量
let value = 1
// 包我们可以理解为 {} 包起来的,这种实际上叫对象字面量
const bao = {
get:function(){
return value;
},
set:function(val){
value = val
}
}
return bao
}
const myFun = fun()
console.log(myFun.get())
myFun.set(2)
console.log(myFun.get(2))
// 无法访问
console.log(myFun.value)
// 无法访问
console.log(fun.value)
如果对闭包感兴趣您可以点击闭包更加详细的解释
级联
有些方法没有返回值此时我们可以返回this就可以启用级联,java高级工程师会把这样的操作称为链式调用。
写法1 - 对象字面量
const state = {
value: 1,
add: function(val) {
const addNum = (val)=>{
this.value = this.value + val
}
addNum(val)
return this
},
min: function(val) {
const addNum = (val)=>{
this.value = this.value - val
}
addNum(val)
return this
}
}
state.add(1)
console.log(state.value)
state.add(2).add(3).min(4).add(1).add(3)
console.log(state.value)
写法2 - 闭包
const fun = function() {
// 函数内部变量
let value = 1
// 包我们可以理解为 {} 包起来的,这种实际上叫对象字面量
const bao = {
get:function(){
return value;
},
add:function(val){
value = value + val
return this
},
min:function(val){
value = value - val
return this
}
}
return bao
}
const myFun = fun()
myFun.add(2).min(3).add(5)
console.log(myFun.get())
还有网上比较容易查到的原型链方式这里就不再列出,可以点击文章顶部的链接进行查看。
柯里化
柯里化允许我们把函数与传递给他的参数相结合,产生一个新的函数。
// 支持多参数传递
function curry(fn) {
const curried = (...rest) =>{
console.log(rest)
if(rest.length < fn.length) {
return (...rest2) => {
return curried(...[...rest,...rest2])
}
} else {
fn(...rest)
}
}
return curried
}
function add (a,b,c) {
console.log(a+b+c)
}
const curryAdd = curry(add)
curryAdd(1)(3)(4)
// 实现一个add方法,使计算结果能够满足如下预期:
// add(1)(2)(3) = 6;
// add(1, 2, 3)(4) = 10;
// add(1)(2)(3)(4)(5) = 15;
const add = (...rest)=> {
const args = [...rest]
const adder =(...rest) =>{
args.push(...rest)
return adder
}
adder.toString = ()=> {
return args.reduce(function (a, b) {
return a + b
})
}
return adder
}
console.log(add(1)(2)(3))
记忆
函数可以将先前的操作结果记录到某个对象里面,从而避免无谓的重复计算。通常也可以叫做缓存。
function fibnan(n) {
const memo = [0, 1]; // 缓存计算结果
const fib = (n) => {
if (memo[n] != null) { // 如果已经被计算就返回他
return memo[n];
}
return (memo[n] = fib(n - 1, memo) + fib(n - 2, memo)); // 否则将他计算加入缓存
};
return fib;
}
总结
通过上列的学习,我们了解了函数的各大调用方法以及函数的一些特性,包括我们对闭包的理解和使用更加透彻,同时也学会了一些函数的高级用法。
- 三种调用方式:方法、函数、构造器
- 四种高级函数使用:级联、柯里化、记忆、闭包