###就不发表个人获奖感言了,直接开始吧,真的被自己菜到了,虽然我以前也不记记不住这玩意~
常规函数:使用function关键字定义,可声明为函数声明和函数表达式
函数声明
good("彩虹海") //你可以在它声明之前调用,函数声明可以提升 function good(name){ return "Hello,"+name }
函数表达式
hello("彩虹海")/*错误示范**/ //这里是无法调用的,会提示说:Cannot access 'set' before initialization,因为它不可以被提升 //只能在被定义后才能被调用 const hello = function(name){ return "Hello,"+name } //这里可能会有人问function后面为什么没有函数名,因为函数表达式可以是匿名函数或具名函数表达式 hello("彩虹海")
常规函数的核心特征
1、动态的this绑定
常规函数的this值取决于调用方式,而非定义时的环境
默认绑定:当直接调用时,非严格模式(这个观念会在下一个文章里说明,也是下一个文章的标题)下this指向全局对象(如游览器中的window),严格模式下为underfined。
function showTip(){ console.log(this) } showTip() //这里在非严格模式下会打印window
隐式绑定:作为对象方法调用时,this指向对象
const obj = { value:"1", getValue:function(){ return this.value //this指向obj } } console.log(obj.getValue())//1
显式绑定:通过call、apply或bind强制指定this
function logThis(a,b,c){ console.log(this.name) console.log(a,b,c) } const person = {name:"123"} logThis.call(person,"tian","di","ren") //123 换行 // tian di ren logThis.apply(person,["tian","di","ren"])//123 换行 // tian di ren const getBind = logThis.bind(person,"tian","di","ren") getBind()//123 换行 // tian di ren //实现效果可以通过调用控制台打印输出查看 /* *这里简单了解下call、apply、bind的用法和区别 */ call:立即触发函数调用,并显式指定this值,后面参数用逗号隔开 apply:立即触发函数调用,并显式指定this值,后面参数用[]包裹 bind:创建一个新的函数,永久绑定this值和部分参数,不立即执行
2、构造函数与new
常规函数可以用new关键字创建对象实例,此时函数被称为“构造函数”,负责初始化实例对象的属性和方法,为实例提供共享的行为模式。###这句话我开始是非常不理解的,不看后面我的解释的话,不知道有没有小伙伴们愿意把你们的思考写在评论区呢?
function Peron(name){ this.name=name } const name="彩虹海" const logName = new Peron(name) //注意,构造函数的语法要求名称首字母必须大写,所以对应函数那里也要大写,不然会提示defined console.log(logName.name) //这里提出我的理解就是组件化开发,构造函数就是一个基础版的组件封装 //特性 构造函数 组件封装 //作用 定义一类对象的通用结构和行为 定义一类UI的通用结构和行为 //复用方式 通过new生成多个实例 通过标签或者函数调用生成多个实例 //这里我理解构造函数是一个仅处理数据和行为的组件,而现代前端组件是扩展了UI和生命周期的超级构造函数
创建构造函数组件,我们需要配合prototype原型来使用,将构造函数内部的行为函数定义到prototype上 ,那么我们哪怕new了一百个实例,也只共用一个行为函数,这大大提高了内存的使用效率。
不过鉴于有了class语法,推荐使用class的语法来写。它本质是构造函数的语法糖,看起来更直观。
class User { constructor(name) { this.name = name; } // 方法自动挂载到原型 sayHello() { console.log(`Hello, I'm ${this.name}`); } } const user = new User('Alice');
使用场景:当存在大量的类似的行为模式时
3、prototype原型和arguments对象
常规函数自动拥有prototype属性,用于实现基于原型的继承;常规函数内部可访问arguments对象,它是一个类数组对象,包含所有传入的参数。但它不是真正的数组,需要转换为数组才能使用数组的方法(Array.from(arguments))
常规函数部分使用场景
1、需要动态this的场景,例如需要this指向对象自身。
2、构造函数,构造函数必须使用常规函数创建实例
3、需要arguments对象的场景,例如处理不定数量参数
4、生成器函数(Generator),例如使用function*语法定义可暂停执行的函数
箭头函数:是ES6引入的语法糖,用=>定义,适用于简洁的表达式
基础语法
//单参数却单行返回时,省略{}和return
const quare = x => x + x;
console.log(quare(3))//6
//多参数或多行语句是,需用()包裹参数,{}包裹函数体
const sum = (a,b) => {
const result = a + b;
return result
}
console.log(sum(4,8)) //12
//如果要复制代码运行,注意命名规范,部分会重复命名。
核心特征
1、this绑定(无独立this),箭头函数的this继承自外层作用域,而非运行时调用方式
const obj ={ value:42, // 常规函数:this指向obj getValue:function(){ return this.value; }, //箭头函数:this继承外层(此处为全局) getValueEs:()=> this.value } console.log(obj.getValue()) //42 常规函数没有问题 console.log(obj.getValueEs()) //underfined 箭头函数this可能指向全局
那它在this这里的表现好像平平无奇啊,那它出现是为了解决什么问题呢?
const obj={ value:42, start:function(){ console.log("外层this value:"+this.value) setTimeout(function(){ console.log("常规函数回调中的this value"+this.value) },1000) } } obj.start() //是的,是为了解决回调中的this丢失问题;而为什么会丢失呢 //因为常规函数中,这里的this由调用方式决定,如setTimeout直接调用函数时,这里this //默认指向的是全局对象 const obj={ value:42, start:function(){ console.log("外层this value:"+this.value) setTimeout(()=>{ //箭头函数this继承外层this,这里指向obj。 console.log("箭头函数回调中的this value"+this.value) },1000) } } obj.start() //而箭头函数,通过词法作用域继承外层this,就避免了丢失问题
2、没有arguments对象:箭头函数无法访问自身的arguments,但可通过剩余参数(...rest)获取参数列表,剩余参数是一个真正的数组,替代了常规函数中的arguments,语法:...参数名,必须放在参数列表的最后。
没有prototype属性:箭头函数没有原型,无法用于原型继承。
不能作为构造函数:箭头函数没有[[Conctruct]]内部方法,无法用new调用。
不可用作Generator函数:箭头函数不能包含yield关键字,无法定义为生成器。
适用场景
1、需要固定this指向的回调函数
2、可以简化的函数
3、替代闭包保存this,常规函数在闭包中需要额外处理this指向,而箭头函数直接词法绑定this
回顾上方内容,我们可以了解到,箭头函数通过词法this解决了回调函数this丢失问题,通过简洁的语法,优化了函数式编程,提高了这些场景下的代码可读性和安全性。但是在需要动态this、构造函数或复杂功能时,常规函数仍是首选。
欢迎朋友们留言补充,我都会认真看完每一条评论的。文章中有很多地方的介绍都还是很表皮的,如果有写的不对的,希望大家能留言告诉我,我会在看到消息的第一时间进行处理。
【抱拳了老铁】