//with 语句,快捷访问对象属性let newObj ={name:"midshar",sex:"male",age:18}with(newObj){//使用 with 语句
name ="changeName"
sex ="changeSex"
age ="changeAge"}
console.log(newObj)//debugger 语句,相当于断点lettFunDebugger=function(val){if(val ===undefined)debugger//使用 debugger 断点,当被传入参数为 undefined 时,启用断点return val **2}
console.log(tFunDebugger())
//在对象中简写函数方法let newObj ={id:1,max(a, b){return a > b ? a : b
},min(a, b){return a > b ? b : a
}}
console.log(newObj.max(999,255))
console.log(newObj.min(999,255))
//在对象中,利用 get 与 set 设置访问器属性,两者必须有相同的属性名let newObj ={id:1,name:'midshar',sex:'male',getchange(){return`${this.name} is a ${this.sex} person.`},setchange(sex){this.sex = sex
}}
console.log(newObj.change)
newObj.change ="After"
console.log(newObj.change)//访问器属性也可以继承let newInheritObj = Object.create(newObj)
newInheritObj.ownProp ='新建自有属性'
newInheritObj.change ="更改继承属性"
console.log(newInheritObj.change)
console.log(newInheritObj)//保证序号严格递增let newToNext ={_nIndex:0,gettoNext(){returnthis._nIndex++},settoNext(index){if(index <this._nIndex){thrownewError(`The value must be bigger than ${this._nIndex}.`)}else{this._nIndex = index
}}}
console.log(newToNext._nIndex)
newToNext.toNext =50
newToNext.toNext
newToNext.toNext
console.log(newToNext.toNext)//返回随机值let resRandom ={getgetRandom(){return Math.floor(Math.random()*1000)//返回0~1000随机值}}
console.log(resRandom.getRandom)
{//使用函数声明,当一个函数没有返回值时,默认返回 undefined//使用函数声明时,在一个JS代码块中(一个js文件中)声明的所有函数在该块的所有地方都有定义,且会被提升至该块最前面newFun_declaration()functionnewFun_declaration(parameter){
console.log("利用函数声明创建函数")}//在严格模式下,在语句块中定义的函数在该语句块中有效,在语句块外无效;非严格模式下,只能在声明定义函数的代码之后使用for(let i =0; i <10; i++){functionprinti(get){
console.log(`在语句块中创建函数=>${get}=>${i}`)}printi('inside')}}printi('outside'){//使用函数表达式,这种方法定义的函数无法被提升,只能先定义,再使用,并且只能在该代码块中使用//函数表达式将函数对象赋值给一个变量/常量constnewFun_1=function(){
console.log("利用函数表达式创建函数")}newFun_1()//函数表达式中的函数可以添加名字,这个名字相当于是函数体内的一个局部变量,并且 newFun_2===facNumconstnewFun_2=functionfacNum(x){if(typeof(x)==='number'){if(x <0){
console.error("参数不能为负数")returnfalse}elseif(x ===1|| x ===0){return1}else{return x *facNum(x -1)}}else{
console.error("参数必须为数字")returnfalse}}
console.log(newFun_2(5))//函数表达式可以作为函数的参数constnewFun_3=function(fun, a, b){fun(a, b)}newFun_3(function(x, y){//定义函数参数
console.log(x > y ? x : y)},999,100)//函数表达式可以在定义完的同时立刻调用const newFun_4 =(function(){
console.log("我是一个立即执行函数")})()//箭头函数,其中的 this 指向不是调用上下文,它没有 thisletnewFun_arrow=()=>{
console.log("我是一个箭头函数")}newFun_arrow()//嵌套函数,在嵌套函数中,内层函数可以访问外层函数的参数,而外层函数无法访问内层函数的参数//在嵌套函数中,this指向要么是全局对象(非严格模式),要么是undefined(严格模式)constnewFun_nest=function(numA, numB){constsquareFun=function(num){return num **2}return Math.sqrt(squareFun(numA)+squareFun(numB))}
console.log(newFun_nest(3,4))//=>5}
4.2函数的形参与实参
{//可选形参与默认值//当传入的实参个数少于函数中定义的形参个数时,多余形参会被默认设置为 undefinedconstnewFun_less=function(num1, num2, num3){
console.log(`num1=${num1}---num2=${num2}---num3=${num3}`)return(num1 > num2 ? num1 : num2)> num3 ?(num1 > num2 ? num1 : num2): num3
}newFun_less(1)//可以为可选参数设置默认值constnewFun_acq=function(num1, num2, num3){
num2 = num2 ||10
num3 = num3 ||100
console.log(`num1=${num1}---num2=${num2}---num3=${num3}`)return(num1 > num2 ? num1 : num2)> num3 ?(num1 > num2 ? num1 : num2): num3
}newFun_acq(1,2)//在 ES6 之后,可以直接在定义函数时在形参中给定默认值constnewFun_direct=function(num1, num2 =666, num3 =999){
console.log(`num1=${num1}---num2=${num2}---num3=${num3}`)return(num1 > num2 ? num1 : num2)> num3 ?(num1 > num2 ? num1 : num2): num3
}newFun_direct(222)//在形参中支持用计算的数值constcreatNewRectangle=(width, height = width *2)=>{
console.log(width, height)}creatNewRectangle(5)}{//使用 ... 在定义函数形参时,这与展开运算符不同//利用剩余形参可以将在调用函数时多余的实参装入一个数组中,将剩余参数作为函数的最后一个参数---变长函数//当不存在剩余参数时,剩余参数是一个空数组constgetMaxNum=function(firstNum,...restNum){
console.log(restNum)let maxNum = firstNum
for(let item of restNum){
maxNum = item > maxNum ? item : maxNum
}return maxNum
}
console.log(getMaxNum(1,5,4,9,7,5,2))
console.log(getMaxNum(1))}{//Arguments 对象constgetMaxNum_argument=function(){
console.log(arguments)if(arguments.length ===0){
console.error("参数不能为空")returnfalse}elseif(arguments.length ===1){return arguments[0]}else{let max = arguments[0]for(let i =1; i < arguments.length; i++){
max = max > arguments[i]? max : arguments[i]}return max
}}
console.log(getMaxNum_argument(12))
console.log(getMaxNum_argument(12,32456,48,534,653,51))}{//在函数调用中使用 ... 扩展操作符,注意与剩余形参的区别functiontimed(f){//返回包装后的函数returnfunction(...argu){//调用 timed() 时返回这一层函数
console.log(arguments)
console.log(`Import function ${f.name} success.`)let startTime = Date.now()try{returnf(...argu)//返回该函数,直接执行,展开搜集的参数,相当于调用 benchmark(5)}finally{
console.log(`Export function ${f.name} spent ${Date.now()- startTime}ms.`)}}}constbenchmark=function(num){let sum =0for(let i =1; i <= num; i++){
sum += i
}return sum
}constgetMinFun=function(a, b){return a > b ? b : a
}
console.log(timed(benchmark)(5))//在函数表达式后接参数调用,argu=[5]
console.log(timed(getMinFun)(666,999))//在函数表达式后接参数调用,argu=[666,999]}{//把函数实参解构为形参//复制数组中的指定长度切片至另一个数组中constcopySliceArray=function({ from, to =[], sliceLength = from.length, fromIndex =0, toIndex =0}){//形参分别表示 {被切片的数组,目的数组,切片长度,被切片数组的起始索引,目的数组的起始索引}
to.splice(toIndex,0,...from.slice(fromIndex, fromIndex + sliceLength))//将运用 slice 方法取得的切片数组解构成为数组元素再插入 to 数组return to
}let toArray =[1,2,3]let fromArray =[999,999,999,999]
console.log(copySliceArray({from: fromArray,to: toArray }))//把数组参数中的多余实参搜集到剩余参数中constnewFun_moreArrayEle=function([x, y,...surlpusEle],...restParameter){
console.log(surlpusEle)
console.log(restParameter)return{X: x,Y: y }}
console.log(newFun_moreArrayEle([1,2,3,4,5,6,7],777,888,999))//把对象参数中的多余属性搜集到剩余参数中,多余参数是一个剩余属性组成的对象constnewFun_obj=function({ x, y, z =0,...rest }, base){return{x: x * base,y: y * base,z: z * base,restProp: rest }}
console.log(newFun_obj({x:100,y:200,z:300,p:555,q:666,m:777,n:888},1))//函数的参数类型,JS中函数的参数不会进行检查,可以手动设置判断constaddAll=function(numArray){if(!(numArray instanceofArray)){thrownewError("参数必须是一个数组")return}let sum =0for(let i of numArray){if(typeof(i)!=='number'){thrownewError("数组所有元素必须是 number 类型")return}
sum += i
}return sum
}
console.log(addAll([1,2,3,5]))}
4.3调用函数
"use strict"{//作为函数调用//条件式调用函数,当调用函数是否是 null 或者 undefined 时,不会执行函数调用const newFun_null =nullconstnewFun_function=function(){
console.log("作为函数调用")}
newFun_function?.()
newFun_null?.()//条件式调用等价于
console.log(newFun_null !==null&& newFun_null !==undefined?newFun_null():undefined)//利用 this 值判断是否是位于严格模式中,在严格模式中,this 是 undefined,非严格模式中,是调用上下文。在箭头函数中, this 总是指向所在环境const isStrictModel =(function(){//当前环境是严格模式时,返回 true,否则返回 falsereturn!this})()
console.log(isStrictModel)//=>true}{//作为方法调用//作为对象的方法被调用时,当存在嵌套函数时,内层函数的 this 值不是调用对象,要么是全局对象 window(非严格模式),要么是 undefined(严格模式)let newObj ={numA:3,numB:4,addFun:function(){
console.log(this)//=> newObjreturninsideSquareFun(this.numA)+insideSquareFun(this.numB)functioninsideSquareFun(val){
console.log(this)//=> undefined(严格模式)return val * val
}}}
console.log(newObj.addFun())}{//作为构造函数调用//当返回值是一个原始值时classPersonInfo{constructor(name, sex, age){this.name = name
this.sex = sex
this.age = age
return//当 return 后不接返回值或者返回一个原始值,将会仍然以新创建的对象作为 this 值}printThis(){
console.log(this)}}let newPerson =newPersonInfo('midshar','male',18)
newPerson.printThis()//当返回值是一个对象时classnotPersonInfo{constructor(name, sex, age){this.name = name
this.sex = sex
this.age = age
return{//如果在构造函数中,return 一个对象,那么实例化的对象将会是这个值,即 this 是这个值prop:"测试属性",test:"测试属性"}}printThis(){
console.log(this)}}let notNewPerson =newnotPersonInfo('midshar','male',18)
console.log(notNewPerson)}{//使用 call() 或者 apply() 间接调用}{//隐式函数调用}
4.4函数式编程
{//使用函数处理数组//传统方式下,计算一个数值数组的平均值 average 与标准差 stdDeviationlet valueArray =[1,1,3,5,5]let total =0let stdDeviation =0let average =0for(let i =0; i < valueArray.length; i++){
total += valueArray[i]}
average = Math.round(total / valueArray.length)//清零总值
total =0for(let i =0; i < valueArray.length; i++){
total += Math.pow(valueArray[i]- average,2)}
stdDeviation = Math.round(Math.sqrt(total /(valueArray.length -1)))
console.log(`average=${average}\nstdDeviation=${stdDeviation}`)//利用 map 方法与 reduce 方法修改上述方法,可以使代码更加简洁let newAverage = Math.round(valueArray.reduce((until, item)=> until + item,0)/ valueArray.length)let afterMapArray = valueArray.map(x=> x - newAverage)let newStdDeviation = Math.round(Math.sqrt((afterMapArray.map(x=> x * x).reduce((until, item)=> until + item,0))/(valueArray.length -1)))
console.log(`newAverage=${newAverage}\nnewStdDeviation=${newStdDeviation}`)}{//高阶函数,高阶函数就是函数的形参是一个或者多个函数,并且返回值也是一个函数//一个返回新函数的逻辑非的函数functionnot(fun){return(...allParameters)=>{let res =fun.apply(this, allParameters)//apply接收数组形式的形参return!res
}}constisEven=(i)=> i %2===0//判断数组元素是否为偶数const isOdd =not(isEven);//判断数组元素是否为奇数
console.log([1,1,1,1].every(isOdd))//=>true//让数组中的每个数值元素自增1functionmap(arr,...arg){//自定义 map 函数,传入一个数组,传入一个方法,将会把数组按照该方法映射为新数组let res = arr.map(...arg)return res
}functionmapper(fun){returnfunction(arr){returnmap(arr, fun)//调用上述自定义 map 函数}}letaddOne=x=> x +1let newArray =[2,2,2,2,2]
console.log(mapper(addOne)(newArray))//=>[3,3,3,3,3]//接收两个函数参数的函数functionfirstSquareLaterAdd(fx, gx){returnfunction(...arg){returnfx.call(this,...gx.call(this,...arg))}}consttFx=(x, y)=> x + y
consttGx=arr=> arr.map(i=> i * i)
console.log(firstSquareLaterAdd(tFx, tGx)([3,4]))//调用函数}{//函数的部分应用//改变传入函数参数的顺序//将内层函数传递的参数放在左边functionputInnerParameterLeft(f,...outParameters){returnfunction(...innerParameters){let args =[...innerParameters,...outParameters]returnf.apply(this, args)//将所有的参数最终都传递给 f 函数}}//将内层函数传递的参数放在右边functionputInnerParameterRight(f,...outParameters){returnfunction(...innerParameters){let args =[...outParameters,...innerParameters]returnf.apply(this, args)//将所有的参数最终都传递给 f 函数}}//将外层 undefined 类型的参数用内层参数替换functionreplaceOutUndefinedParameters(f,...outParameters){returnfunction(...innerParameters){let outP =[...outParameters]let innerP =[...innerParameters]let innerIndex =0for(let i =0; i < outP.length; i++){if(outP[i]===undefined){
outP[i]= innerP[innerIndex++]}}let args =[...outP,...innerP.slice(innerIndex)]returnf.apply(this, args)}}//使用上述定义的函数constmethod=function(x, y, z){return x *(y - z)}
console.log(putInnerParameterLeft(method,2)(3,4))//=>3*(4-2)=>6
console.log(putInnerParameterRight(method,2)(3,4))//=>2*(3-4)=>-2
console.log(replaceOutUndefinedParameters(method,undefined,3)(4,5))//=>4*(3-5)=>-8}
{//函数作为值被使用//在 JS 中,函数不仅是语法,也是值//使用函数声明创建函数,表示将函数对象赋值给了声明的标识符functionnewFun_declaretion(){
console.log("我是声明式函数")}newFun_declaretion()const newFun_declaretion_copy = newFun_declaretion
newFun_declaretion_copy()//将函数复制给另一个变量//函数作为函数的形参1constadd=(x, y)=> x + y
constsub=(x, y)=> x - y
constmul=(x, y)=> x * y
constdiv=(x, y)=> x / y
constoperationNum=function(fun, numA, numB){returnfun(numA, numB)}
console.log(operationNum(add,operationNum(sub,8,3),operationNum(add,2,3)))//=>10//函数作为函数的形参2const operations ={add:(x, y)=> x + y,sub:(x, y)=> x - y,mul:(x, y)=> x * y,div:(x, y)=> x / y,mod:(x, y)=> x % y,pow:(x, y)=> x ** y
}constoperation_objMethods=function(operate, numA, numB){if(typeof(operations[operate])==='function'){return operations[operate](numA, numB)}else{thrownewError("不是一种方法!")}}
console.log(operation_objMethods('add',999,1))//定义自己的函数属性constcomputed_Factorial=function(val){if(Number.isInteger(val)&& val >0){//当传入的参数是个正整数时if(!(val in computed_Factorial)){//像数组一样索引并在索引处缓存
computed_Factorial[val]= val *computed_Factorial(val -1)}return computed_Factorial[val]}elseif(val ===0){//传入参数为 0 时return computed_Factorial[0]=1}else{returnNaN}}
console.log(computed_Factorial(5))
console.log(computed_Factorial[4])//读取之前存入的属性}
4.7闭包
{//在 JS 中,函数是使用定义它们的作用域来执行的,嵌套函数的作用域是外层函数之内,所以它能够访问同一定义域上的变量//在闭包中,外层函数一般返回一个函数对象let newFun =(function(){//定义一个立即执行函数来作为命名空间存放私有变量let count =0//私有变量functionuniqueInteger(){return count++}return uniqueInteger//返回一个函数}())
console.log(newFun())//newFun() 小括号表示调用 uniqueInteger 函数
console.log(newFun())//在一个私有领域中,可以由多个闭包共享,每调用一次 counter() 就会新创建一个私有变量,并且只供自己的调用者使用,多个调用者之间的私有变量是独立的,不会互相影响functioncounter(){let count =0constaddCount=()=> count++constresetCount=()=> count =0return{
addCount,
resetCount
}}let counter_1 =counter()//创建计数器1let counter_2 =counter()//创建计数器2
console.log(counter_1.addCount())
console.log(counter_1.addCount())
console.log(counter_1.addCount())
console.log(counter_1.addCount())
console.log(counter_2.addCount())
console.log(counter_2.addCount())
counter_2.resetCount()//重置第二个计数器
console.log(counter_1.addCount())
console.log(counter_1.addCount())
console.log(counter_1.addCount())}{//使用闭包定义对象上的 set() 与 get() 方法constnewFun_set_get=function(i){//定义一个只允许递增的函数return{getuniqueAdd(){//只允许内层函数访问 ireturn i++},setuniqueAdd(j){if(j < i){//只允许内层函数访问 ithrownewError(`参数必须大于${i}`)}else{
i = j
}}}}let newC =newFun_set_get(100)//初始化
console.log(newC)
console.log(newC.uniqueAdd)
console.log(newC.uniqueAdd)
console.log(newC.uniqueAdd)
newC.uniqueAdd =200//更改计数值
console.log(newC.uniqueAdd)
console.log(newC.uniqueAdd)
console.log(newC.uniqueAdd)}{//使用闭包的私有属性访问器方法functionaddPrivateProp(Obj, propName, checkMethod){let value//设置一个私有属性
Obj[`get${propName}`]=function(){return value
}
Obj[`set${propName}`]=function(p){if(!checkMethod(p)){thrownewTypeError("无效的属性名!")}else{
value = p
}}}//以下是使用方法let newBlankObj ={}addPrivateProp(newBlankObj,'Name',x=>typeof(x)==='string')//为 newBlanObj 对象添加属性访问器方法
newBlankObj.setName("newProp")//设置私有属性
console.log(newBlankObj)
console.log(newBlankObj.getName())//读取私有属性}{//创建一个常量数组constcreateNum=function(val){return()=> val
}let newArray =[]for(let i =0; i <10; i++){
newArray[i]=createNum(i)}
console.log(newArray[5]())//=>5//错误的方式functionconsfuns(){let funcs =[]for(var i =0; i <10; i++){//在这里,使用 var 关键字与使用 let 关键字的结果不一致
funcs[i]=()=> i//嵌套函数}return funcs
}let funcs =consfuns()
console.log(funcs[5]())//=>10//在这里,当循环结束时,变量 i 的值为 10,此时已经创建了一个含有十个函数的数组,每个数组返回一个 i 值,//数组中的这些函数是嵌套函数,有权访问父作用域定义的 i 值,此时 i = 10,当调用这些函数时,都会返回 10}
4.8函数的属性、方法与构造函数
{//在 JS 中,函数是一种特殊的对象//函数有一个只读的 length 属性,表示在声明的参数列表中的形参个数,剩余形参不包含在内constgetMax=function(a, b){return a > b ? a : b
}
console.log(getMax.length)//=>2//函数有一个只读的 name 属性,对于匿名函数,其值为第一次创建函数时付给函数的变量名或属性名
console.log(getMax.name)//=>getMax//每个函数都有 prototype 属性
console.log({ getMax })//=>{}}{// call() 方法与 apply() 方法//call()方法,用法是,将需要绑定到对象上的方法/函数调用 call() 方法,call() 方法的第一个参数为该对象,后续参数为方法的参数let newObj ={id:1,name:'midshar',age:18}//将 getMin() 作为对象 newObj 的方法调用constgetMin=function(m, n){return m < n ? m : n
}let smallest =getMin.call(newObj,111,222)
console.log(smallest)//=>111//apply() 方法,用法是,将需要绑定到对象上的方法/函数调用 call() 方法,call() 方法的第一个参数为该对象,后续参数为方法的参数,参数的形式是数组constgetMax=function(a, b){return a > b ? a : b
}let biggest =getMax.apply(newObj,[222,999])
console.log(biggest)//=>999//将对象中的方法替换为另外一种包装版本let newObj_test ={id:1,name:'midshar',packFun:(a)=> a.slice(0,1).toUpperCase()+ a.slice(1)}functionpackageFun(obj, method){let original = obj[method]//保存原始方法的副本
console.log(original)
obj[method]=function(...argu){//开始包装函数
console.log((newDate).toLocaleString(),`original function:==>`, method)let res =original.apply(this, argu)//按照原始方法执行
console.log((newDate).toLocaleString(),`result function:==>`, method)return res//返回执行结果}}packageFun(newObj_test,'packFun')
console.log(newObj_test.packFun)}{//bind() 方法,主要目的是把函数绑定给对象,这个方法会返回一个新的函数,新函数的 name 属性由 bound 与调用 bind() 方法的函数 name 属性组成//为对象绑定新的函数functionbindFun(value){returnthis.num + value//this 在绑定后会指向绑定该方法的对象}let newObj ={num:999}let newBindResFun =bindFun.bind(newObj,1)//将返回的新函数赋值给变量,bind() 方法第一个参数是需要绑定的对象,后续参数作为新函数的参数
console.log(newBindResFun.name)//=>bound bindFun
console.log(newBindResFun())//=>1000//利用 bind 执行部分应用,表示在第一个参数之后传给 bind 的值会随着 this 值一起被绑定letsum=(a, b)=>{return{a: a,b: b }}let res_sum =sum.bind({x:10,y:5},666)//将参数a(第一个)绑定成为666
console.log(res_sum())letother=function(b, c){//注意这里不能使用箭头函数return{this:this.num,b: b,c: c }}let res_ohter =other.bind({num:222},111)//绑定this值与参数b
console.log(res_ohter())letall=function(k, f, c){return{k: k,f: f,c: c
}}let res_all =all.bind(null,444,555)//绑定k与f
console.log(res_all(999))//999将赋值给未绑定的c}{//Function 构造函数,可以接收任意多个参数,最后一个参数表示函数体的内容,其余参数表示函数接收的形参。若不接收形参,可以直接传入一个函数体文本的字符串。//利用 Function 构造函数创建的是匿名函数
console.log((newFunction('x','y','return x>y?x:y;'))(5,9))//为匿名构造函数加入名字let noNameFun =newFunction('x','y','return x>y?x:y')
console.log(noNameFun(5,3))//需要注意,Function 构造函数创建的函数不会遵循词法作用域的规则,它会始终被编译为顶级作用域的函数let scope ='global'functionnewFun_top(){let scope ='local'const insideFun =newFunction('return scope')return insideFun
}try{
console.log(newFun_top()())}catch(err){
console.log(err)}}