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) //18apply / call 调用:使用apply方法可以改变this的指向。如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象)。箭头函数:箭头函数中没有this绑定,必须通过查找作用域链来决定其值。如果箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数的this;否则,this的值会被设置为undefined。
资料参考
转载自:https://juejin.cn/post/6854573220172365832