call实现
Function.prototype.hycall = function (thisarg,...agrs) { //接受剩余参数
var fn = this; //隐式绑定
thisarg = (thisarg !=== null && thisarg !== undefined) ? Object(thisarg) : window; //判断传入this
thisarg.fn = fn;
var re = thisarg.fn(...agrs); //展开args
delete thisarg.fn
return re;
}
function foo () {
console.log('1',this)
}
function sum () {
console.log('2',this)
}
foo.hycall({})
sum.hycall({})
apply实现
Function.prototype.hyapply = function (thisarg,...argArray){
var fn = this
thisarg = (thisarg !=== null && thisarg !== undefined) ? Object(thisarg) : window;
thisarg.fn = fn;
args = argArray || []
var result;
if(!args){
result = thisarg.fn(args)
}else {
result = thisarg.fn(...args)
}
delete thisarg.fn
return result;
}
Function.prototype.hyapply = function (thisarg,argArray){
var gn = this
thisarg = (thisarg !=== null && thisarg !== undefined) ? Object(thisarg) : window;
thisarg.fn = fn;
var result;
if(!argArray){
result = thisarg.fn()
}else{
result = thisarg.fn(...argArray)
}
delete thisarg.fn
return reuslt;
}
forEach
//forEach修改原对象值
const arr = [1,2,3]
arr.forEach(item=>item+1);
console.log(arr); //此时不可修改
arr.forEach((item,index,arr)=>arr[index]+=1); //此时arr为堆内存地址,直接修改地址内的
console.log(arr); //[2,3,4]
//forEach修改复杂类型对象值
const arr = [
{age:18},
{age:19},
{age:20}
]
arr.forEach(item=>{
item.age += 1; //此时item为堆内存地址
})
console.log(arr); //[{age:19},{age:20},{age:21}]
//forEach可以被打断吗?可以
const arr = [1,2,3]
arr.forEach(item=>{
console.log(item); //1
throw new Error('error message')
}) //报错打断,仅执行一次
//报错但并不打断
arr.forEach(item=>{
try{
console.log(item)
throw new Error('error message')
}catch(error) {
console.log(error)
}
})
bind实现
Function.prototype.hybind = function(thisarg,...argsArray){
var fn = this;
thisarg = (thisarg !== null && thisarg !== undefined)? Object(thisarg):window;
function proxyFn(...args){
thisarg.fn = fn
var finalArray = [...argsArray,...args]
var result = thisarg.fn(...finalArray)
delete thisarg.fn
return result
}
return proxyFn
}
arguments
arguments是一个类数组(array-likr)的对象,无法使用数组的方法。
常见操作
function foo(num1,num2){
console.log(arguments); //输出传入的所有实参
console.log(arguments.length); //输出传入实参的长度
console.log(arguments[index]); //输出arguments对应索引值的实参
console.log(arguments.callee); //输出该函数;函数本身
console.log(typeof arguments.callee); //function
}
foo(2,3,4,5)
arguments转数组
function foo(num1,num2){
//1.自己遍历
var newArr = []
for(let i = 0 ; i < arguments.length ; i++){
newArr.push(arguments[i]);
}
console.log(newArr); //输出数组与arguments值相同
//2.使用数组原型slice方法
var newArr = Array.prototype.slice.call(arguments);
console.log(newArr)
//3.使用数组.call转换
var newArr = [].slice.call(arguments);
console.log(newArr)
//4.使用Array.from
var newArr = Array.from(arguments);
//5.使用展开运算符
var newArr = [...arguments];
}
Array.prototype.slice实现原理
Array.prototype.hyslice = function (start,end) {
arguments = this
var start = start || 0;
var end = end || arguments.length
var newArr = [];
for (let i = start; i < end; i++) {
newArr.push(arguments[i]);
}
return newArr;
};
箭头函数没有arguments,使用时会自动想上层作用域查找,如果到全局作用域,在浏览器端是没有arguments的,但是在node中会有arguments
在node中,会把js文件当作一个模块,这个模块会被包裹在一个函数中,当执行时,则使用call调用这个函数;.call({},...)
function foo(){
var bar = () => {
console.log(arguments); //此时arguments为foo的arguments
}
return bar
}
var fn = foo(123)
fn();
箭头函数中更推荐使用剩余参数
var foo = (...arg) => {
console.log(arg)
}
foo(1,2,3)
纯函数
- 确定的输入,一定会产生确定的输出
- 函数在执行过程中,不能产生副作用
在开发中尽量选择纯函数做开发
//slice由于splice对比
//slice是一个纯函数,并不会修改原来的数组
var arr = [1,2,3]
var arr2 = arr.slice(0,1); //从第0位开始,截至到1位之前,不包含1位
console.log(arr2,arr); // [1] [1,2,3]
//splice会原数组进行修改
var arr3 = arr.splice(2); //原数组保留2位,其余全部分配给新数组
console.log(arr3,arr); //[3] [1.2]
纯函数修改变量值
var obj = {name:'xiaohuang',age:20};
function foo(info){
return {...info,gender:'male',height:'200cm'}
}
obj = foo(obj)
console.log(obj); //{name:'xiaohuang',age:20,gender:'male',height:'200cm'}
函数的柯里化(currying)
//常见函数
function foo(x,y,z){
return x+y+z
}
var result = foo(10,20,30); //普通函数
//将多个参数转换为单一函数处理单一参数的过程被称为柯里化
function foo(x){
return function(y){
return function (z){
return x+y+z
}
}
}
var result = foo(10)(20)(30); //此时这个过程被称为柯里化
//简化柯里化
var foo = x => y => z => x + y +z
//等价于下面的箭头函数
var foo = x => y => z =>{
return x + y + z
}
var foo = x =>{
return y => {
return z {
return x + y + z
}
}
}
柯里化的优势一 单一职责原则
function sum (x,y,z){
x += 2;
y *= 2;
z *= z;
return x + y +z
}
console.log(sum(1,2,3))
//复杂柯里化
function foo(x){
x += 2;
return function(y){
y *= y;
return function (z){
return x+y+z
}
}
}
console.log(foo(1)(2)(3));
柯里化的逻辑复用
function makeAdder(count,num){
return count*count + num
}
//如果count为一个常量或固定的数,那每次调用都需要重复写该参数
//柯里化优化
function makeAdder(count,num){
count *= count;
return function (num){
return count + num
}
}
var adder5 = makeAdder(5);
console.log(adder5(10))
var adder10 = makeAdder(10);
console.log(adder10(20))
柯里化的逻辑复用2
function log(date,type,message){
console.log(`[${date.getHours()} : ${date.getMinutes()}][${type}][${message}]`)
}
//常见调用
log(new Date(),'DEBUG','222333')
//定制化
var log = date => type => message => {
console.log(`[${date.getHours()} : ${date.getMinutes()}][${type}][${message}]`)
}
var nowDate = log(new Date()); //固定获取当前时间
nowDate('DEBUG')('23333');
var nowType = log(new Date())('DEBUG'); //固定获取当前时间与类型
nowType('23333');
柯里化的实现
function hyCurrying(fn){
return function curried(...args){
if(args.lenght >= fn.length){
return fn.apply(this,args)
}else{
return function (...args2){
return curried.apply(this,[...args,...args2])
}
}
}
}
组合函数
function double(count){
return count * 2
}
function square (count){
return count**2
}
//普通常用
var re = square(double(count))
//组合函数
function and(m,n){
return function(count){
return n(m(count))
}
}
作用域补充
with语句
JS中可以形成作用与的有全局,函数以及with,对象并不生成作用域;不建议使用
严格模式下会报错
var obj = {message:'Hello,World'}
with(obj){ //将obj作为作用域传入
console.log(message); //'Hello,World'
}//默认应该输出undefined,但实际输出并不是
eval方法
将字符串作为js代码执行;不建议使用
var jsString = 'var message = "hello";console.log(message);'
eval(jsString); //hello
对Object属性的控制 Object.defineProperty(obj,prop,descriptor)
obj为要定义属性的对象;prop为所要定义的属性;descriptor为属性描述符
-
数据属性描述符
Configurable
默认为false,为属性是都为可配置的;为false时该属性不可修改
Object.defineProperty(obj,'height',{value:'200cm',configurable:false})
Enumerable
该属性是否可枚举,属性描述符为空时false,false不可枚举
Object.defineProperty(obj,'height',{value:'200cm',Enumerable:false})
Writeable
该属性是否可赋值(写入),false不可写入
Object.defineProperty(obj,'height',{value:'200cm',Writeable:false})
-
存储属性描述符
Configurable
Enumberable
get
set
Object.defineProperty(obj,'height',{ configurable:true, enumberable:true, get : function (){ return this.value }, set : function(value){ this.height = value } })
定义多个属性描述符
Object.defineProperty(obj, name :{value:'xi'}, age : {value : 20}, gender : {value : 'male'} ) //甚至可以这样填写 var obj = { _age : 18, set age(value){ this._age = value }, get age(){ return this_.age } }
获取对象属性描述符
Object.getOwnPropertyDescriptor(obj,'name') //获取单个
Object.getOwnPropertyDescriptors(obj) //获取多个
禁止对象继续添加新的属性
Object.preventExtensions(obj); //限制
Object.seal(obj); //限制对对象的删除
Object.freeze(obj); //冻结,让属性不可修改,writeable:false
创建对象的方案-工厂模式
function createPerson(name){
var p = {}
p.name = name
return p
}
var p1 = createPerson('xi'),
var p2 = createPerson('bi'),
console.log(p1,p2) ; //{ name: 'xi' } { name: 'bi' }
缺点:无法获取对象的真实属性