开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情
《JavaScript 语言精粹》 第四章函数读完后的一些笔记摘录
本章主要讲解了函数得相关属性,函数的创建、调用、参数、返回值、作用域、闭包、柯里化、递归、回调、模块化、缓存、类型的扩充(原型上加属性)、异常处理(try catch)、级联(链式调用)
函数
- 函数也是对象,也有一个原型对象
Function.prototype,该原型对象的原型对象指向的是Object.prototype - 这个原型的对象上还有一个
constructor属性,该属性值就是该函数本身 - 函数创建时会有两个隐藏属性:函数的上下文和实现函数的行为代码
- 函数可以被保存在变量、对象、数组中
- 函数拥有方法
- 函数可以变成参数,传递给其他函数,还可以作为函数的返回值返回
- 函数在执行时候会有两个附带的参数:
this和arguments
函数字面量
- 创建函数关键字
function - 函数名称,可用于递归,
function name - 函数参数,参数之间用逗号分隔,
function name (a,b,c) - 函数主体用一对花括号包围
function name (a, b , c) { return a + b + c }
函数的调用
- 方法调用
- 构造函数调用
- 函数调用
- 改变
this指向调用apply, call, bind
- 方法调用
当函数作为一个对象的属性时,我们称为这个函数时这个对象的方法,对象调用方法的时候函数内的this指向当前调用它的对象,调用形式有两种:点.和 方括号[]
var obj = {
name: 'abc',
getName: function(){
console.log(this.name)
},
"set-name": function(name) {
this.name = name
}
}
// 方法调用
obj.getName() // abc
obj['set-name']('ccc')
obj.getName() // ccc
- 函数调用
定义一个函数之间调用,这个时候函数内部的this指向的window。
function fn(){
console.log(this)
}
fn() // window
某些情况下当执行对象方法后得到一个函数返回值 ,再执行这个函数,函数的this并不是指向这个对象,我们来看下
var obj = {
getFn: function(){
function b(){
console.log(this)
}
return b
}
}
var c = obj.getFn()
c() // window
原因是 c 执行的时候其实是执行得 window.c,全局变量调用方法,this 执行全局变量。想要 b 获取到 obj 对象,就想要一个变量来储存,而这一行为就造就了闭包
var obj = {
getFn: function(){
var that = this
function b(){
console.log(that)
}
return b
}
}
var c = obj.getFn()
c() // obj
- 构造函数调用
一般如果在一个函数前面使用new操作符来调用的话,就会把这个函数当做是一个构造函数。new出来的新对象的原型对象指向的就是这个构造函数的prototype属性
扩展:new 操作符的执行过程:
- 在内存中创建一个新对象
- 这个新对象的内部
[[Prototype]]即(__proto__)特性被赋值为构造函数的prototype属性 - 构造函数内部的
this被赋值给这个新的对象(this指向新对象) - 执行构造函数的内部代码(给这个新对象添加属性)
- 如果构造函数返回非空对象,则返回该对象;否则就返回刚刚创建的新对象
function myNew() {
if (arguments.length === 0) {
throw 'error';
}
let obj = {};
let constructor = Array.prototype.shift.call(arguments);
if (typeof constructor !== 'function') {
throw 'error constructor is not function';
}
obj.__proto__ = Object.create(constructor.prototype);
let result = constructor.apply(obj, arguments);
let flag =
result && (typeof result === 'object' || typeof result === 'function');
return flag ? result : obj;
}
apply, call, bind调用
apply, call, bind都是用来改变this指向的方法,bind是返回一个函数,第一个是被绑定的对象,第二个参数如果是apply调用的就是一个数组,其他的都是一个参数列表。
参数 arguments
- 一个对象,拥有
length属性的类数组 - 不能调用任何数组方法
- 函数可以通过
arguments来获取被调用时传递的参数列表 - 包括那些没有被分配给函数声明时定义的形式参数的多余参数
返回
- 函数返回使用
return关键字 - 若未指定返回内容,则返回
undefined return关键字后续的代码将不会被执行
异常
- 函数执行中抛出异常可以使用
throw关键字 - 也可以使用
try catche来捕获异常
扩展功能
- 在
Function.prototype属性上定义属性和方法 - 每个函数都可以获取到新添加的属性和方法
递归
- 直接或者间接的调用自身的一种函数
- 将问题分解为组相似的子问题
// 斐波那契数列
function fn(n) {
if(n < 2){
// 最小化的子问题
return n
} else {
return fn(n-1) + fn(n-2)
}
}
作用域
- 作用域控制变量与参数的可见性及生命周期
- 函数中参数和变量,在函数外部是不可见的
- 外部无法访问内部的变量
- 内部可以访问外部任何变量
闭包
闭包指的是那些引用了另一个函数作用域的变量的函数,通常是在嵌套函数中实现的。
function fn(){
var value = 0
return {
getValue: function(){
return value
},
setValue: function(){
value += 1
}
}
}
var obj = fn()
obj.getValue() // 0
obj.setValue()
obj.getValue() // 1
对象 obj 的getValue 方法可以访问到函数fn内部的变量value,这样的形式就是闭包。虽然对象obj在函数外部,但是依然具有对函数作用域变量的访问权限。
回调
回调函数在日常开发中用的比较多的,最多的就是用在异步请求中,当我们向服务器发送一个请求后,通过回调函数来处理返回的结果,这样即不会阻塞 JS 主线程的运行,也很好的处理异步请求返回的结果后的处理。
ajax(data, function(res){
// 处理返回结果
})
模块
- 模块就是对外暴露出一个接口,也可以理解成一个对象。
- 可以用函数和闭包来创建被绑定对象与私有成员的关系
- 私有化变量,隐藏内部状态和实现方式
- 开发中一个 js 文件就可以看作是一个模块
- 通常结合单例模式使用
级联
级联就是对象调用方法可以采用链式调用,用jquery来表现一下
$('#app').addClass('active').css(...)
对象上有很多的方法,而在每个方法内部最终把指向对象的 this 返回出去就可以实现链式调用。
柯里化
- 把多参数的函数转换成一系列单参数函数并且调用的技术
- 函数的职责单一
- 函数逻辑复用
- 减少函数的传参 举一个简单的例子,拼接网站地址url,通常我们会这样做
function concatUrl(a, b, c){
return a + b + c
}
concatUrl('https://','lodash.com/','docs/4.17.15#curry')
通过柯里化我们可以这样写
function concatUrl(a){
return function (b, c){
return a + b + c
}
}
var httpUrl = concatUrl('https://')
httpUrl('lodash.com/','docs/4.17.15#curry')
通过上述柯里化操作,后面拼接的 url 就不用再传递 https://
记忆
函数可以将先前的操作结果记录在某个对象里面,避免重复操作,这样的优化叫记忆。其实就是缓存数据,判断时候有缓存数据,有就直接用不用再做多余的计算操作。
还是举例斐波那契数列,它是一个数字是之前两个数字的之和。求一个 5 的数列和一个 10 的数列,其实在求 10 的这个数列也求过了刚刚的 5 的数列,所以刚刚在求 5 的时候,把它缓存起来再算 10 的时候直接拿过来用就可以了,这样就减少了很多次的计算。所以在计算的时候把结果都缓存起来,下一次计算就会减少很多计算调用。
var fibonacci = (function{
var meno = [0, 1]
var fib = function (n) {
var result = meno[n]
if (typeof result !== 'number') {
result = fib(n - 1) + fib(n - 2)
meno[n] = result
}
}
return fib
}())
编写一个带记忆功能的函数
var memoizer = function (memo, formula) {
var redur = function (n) {
var result = memo[n];
if (typeof result !== "number") {
result = formula(redur, n);
memo[n] = result;
}
};
return redur;
};