javascript 递归、闭包、this对象
javascript 递归、闭包、this对象
递归
什么是递归函数?
- 编程语言中,函数直接或间接调用函数本身,则该函数称为递归函数。
递归特性
:
- 必须要有一个明确的结束条件
- 每次进入更深一层次递归时,问题规模比上一次递归都应有所减少
- 递归效率不高,递归层次过多会导致栈溢出
arguments.callee
指向正在执行的函数的指针。因此可以用它实现对函数的递归调用。但是在严格模式
use strict
下,不能访问arguments.callee ,访问时会导致致命错误。可以使用命名函数表达式来替代。
function sum(num){
if(num-1){
return 1;
}else {
return num * arguments.callee(num-1);
}
}
'use strict'
var sum = (function f(num){
if(num-1){
return 1;
}else {
return num * f(num-1);
}
})
闭包
什么是闭包?
- 是指有权访问另一个函数作用域中的变量的函数。
- 能够读取其他函数内部变量的函数。
闭包的三个特性
- 函数嵌套函数
- 内部函数可以引用外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
常见闭包的实现?
在一个函数内部,创建另一个函数,并return这个函数;
闭包为什么可以实现在函数外读取到函数内的变量?
因为内部函数的作用域链中包含外包函数的作用域。
f1是f2的父函数,f2被赋给了一个全局变量,f2始终存在内存中,f2的存在依赖f1,因此f1也始终存在内存中,不会在调用结束后,被垃圾回收机制回收。
function f1(){ var n = 123; function f2(){ //f2是一个闭包 alert(n) } return f2; }
闭包产生的原因?
首先要明白作用域链的概念,其实很简单,在ES5中只存在两种作用域
全局作用域
和函数作用域
,当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链
,值得注意的是,每一个子函数都会拷贝上级的作用域,形成一个作用域的链条
闭包产生的本质
就是,当前环境中存在指向父级作用域的引用
闭包的作用(用途场景)
匿名自执行函数
:创建一个匿名的函数,并立即执行
它,由于外部无法引用它内部的变量,因此在函数执行完后会立刻释放资源
,关键是不污染全局对象
。
对结果进行缓存
:闭包可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。
var fn=(function(){ var cache={}//将结果缓存到该对象中 return function(){ var str=JSON.stringify(arguments); if(cache[str]){//判断缓存中是否存在传递过来的参数,存在直接返回结果,无需计算 return cache[str]; }else{//进行计算并返回结果 var sum=0; for(var i=0;i<arguments.length;i++){ sum+=arguments[i]; } return cache[str]=sum; } } })()
模拟私有方法
:私有方法有利于限制对代码的访问,而且可以避免非核心的方法干扰代码的公共接口,减少全局污染。
# 模块模式 var calculator = (function(){ var a = 1; function addCalculator(val){ a += val } return { add1:function() { addCalculator(1); }, add2:function() { addCalculator(2); }, result:function() { return a } } })(); console.log(calculator.result()); // 1 calculator.add1(); console.log(calculator.result()); // 2 calculator.add2(); console.log(calculator.result()); // 4
闭包的优缺点
- 优点
- 可以读取函数内部的变量.
- 可以让局部变量保存在内存中,实现变量数据共享,希望一个变量长期存储在内存中。
- 避免全局变量的污染
- 私有成员的存在。
- 缺点
- 闭包会使得函数中的变量保存在内存中,一直没释放,会导致内存占用很大,
造成内存泄漏
。- 闭包会在父函数外部,
改变父函数内部变量的值
。当父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
this对象
this
对象是在运行时基于函数的执行环境绑定的:在全局函数中,this
等于windows
,而当函数被作为某个对象的方法调用时,this
等于那个对象。如果箭头函数被非箭头函数包含,则
this
绑定的是最近一层非箭头函数的this
,且不能通过call()
、apply()
或bind()
方法来改变this
的值。
普通函数调用
:this
是指向window
的.作为对象的方法调用
:this
这时就指向调用它的对象。构造函数的调用
:如果不使用new 操作符
,则this指向window,如果使用new 操作符创建
了一个实例,则this指向该实例。那么
new 构造函数
做了什么?- 创建新对象
- 把构造函数的作用域赋给新创建的对象。因此
this
指向了这个新对象 - 执行构造函数中的代码,为新对象添加属性 ;
- 返回新对象。
自己实现
new 操作符功能
# 1、创建一个对象并返回;2、修改该对象的this指向;3、保证该对象拥有构造函数的所有属性; function _new(){ var obj = {} var constuctorFun = [].shift.call(arguments) obj.__proto__ = constuctorFun.prototype var res = constuctorFun.apply(obj,arguments) return res instanceof Object ? res: obj } function People(name,age){ this.name = name; this.age = age; } let people2 = _new(People,'Rose',18);//调用自定义create实现new console.log(people2.name) //Rose console.log(people2.age) //18
apply / call 调用
:使用apply
方法可以改变this
的指向。如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象)。箭头函数
:箭头函数中没有this
绑定,必须通过查找作用域链来决定其值。如果箭头函数被非箭头函数包含,则this
绑定的是最近一层非箭头函数的this
;否则,this
的值会被设置为undefined
。
资料参考
转载自:https://juejin.cn/post/6854573220172365832