JavaScript的一些杂七杂八(学习笔记草稿本,想起来啥就写啥)

匿名函数如何实现递归:

使用arguments.callee,arguments 的主要用途是保存函数参数, 但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。

// 计算阶乘
(function(x){  
   return x == 0 ? 1 : x * arguments.callee(x-1);  
})(10);//3628800 

柯里化:

柯里化说白了就是把一次性传入一堆参数的函数,转化成可以一层一层往里传递参数的形式,这么做的目的就是在某些场景下当前几个参数都一直保持相同的时候,就可以把这些调用单独摘出来形成一个新的函数,接下来只用新函数传后面的几个参数就可以了。

这就要求预先将函数的某些参数传入,存在闭包里,返回得到一个简单的函数,

因此会有一些奇特的特性。比如:

var adder = function(num){  
    return function(y){  
       return num + y;    
    }  
}  
   
var inc = adder(1);  //inc是使一个数递增的新函数
var dec = adder(-1); //dec是使一个数递减的新函数

这个地方就有这么一道非常经典的前端面试题:

实现add,满足:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) =15;

add就是实现了柯里化的函数,在某些场景下如果发现要传入的前三个参数总是1,2,3,那么就可以把add(1)(2)(3)赋值给一个新的函数add123,以后再调用就可以直接add123(4)(5)了

下面这个代码主体来自于weixin_33895475 ,原出处找不到了,我这边做了一点优化,实际上不需要递归,只需要每次调用都把当前调用的参数存进闭包就可以了。

function add () {
  // 用来存储所有参数(arguments本身是个对象),第一次调用的时候以第一个括号里的内容为初始化
  var args = Array.prototype.slice.call(arguments);
  
  //获取后续所有括号里的所有参数,存进闭包
  var getArgs = function () {
    args.push(...arguments);
    //注意这里一定要返回getArgs,因为后面有多少个括号不一定,这里就需要返回一个存储arguments的function,不然最多只能读两个括号就报错了
    return getArgs;
  };
  
  //当调用valueOf方法的时候计算总数
  getArgs.valueOf = function () {
    //计算总数
    return args.reduce(function (a, b) {
      return a + b;
    });
  };

  // 或者通过重写toString的方式
  // getArgs.toString=function(){
  //   return args.reduce(function (a, b) {
  //     return a + b;
  //   });
  // };
  
  //初始化之后把这个获取后续所有括号的参数的万恶之源返回
  return getArgs;
}

console.log(add(1,2,3)(4)(5).valueOf()); //输出15

// 重写toString输出方式
// console.log(add(1,2,3)(4)(5)); //输出15

//其实像这样随心所欲的调用也是合法的:
//add(1)(2)(3,4,5)(6,7)()(8)(9,10,11) // 输出66

注意这里有些网上的代码是重写了toString方法,输出的时候直接console.log(add(1,2,3)(4)(5))也能输出15,很多人不理解这个东西怎么被调用的。原因是这样,当我们执行完add(1,2,3)(4)(5)时,此时的结果是什么?很明显,此时的结果依然是一个function,因为我们可以继续往后面接括号继续往下运行。此时对一个function进行console.log会发生什么?实际上会触发这个function的toString方法把它转化成字符串。那么此时我们重写getArgs的toString方法,让它被调用的时候做一点其他的功能比如计算总和,就显得合情合理了。虽然我个人感觉这样挺不舒服的。

这里补充一点.reduce的用法,reduce后面的那个方法可以最多传进去四个参数prev,cur,index,arr。
prev表示保存的之前计算的临时结果,第一次循环的时候prev将是args数组的第一个值。
cur表示当前的要计算的数,第一次循环的时候这个值将是args数组的第二个值,然后在function里计算后,结果存到prev,再进行下一次计算。
index表示cur的下标,初始是1,也就是说循环计算的次数一定比数组长度小1。毕竟只有一个数的时候也没什么好算的。
arr,表示被计算的数组,代码中通过args.reduce调用,则arr就是args。
这四个参数的名字都不是固定的,可以随便起名字。

说白了就是数组取出第一个数存入prev,和第二个数cur进行function里的计算,计算结果覆盖掉prev,cur赋值为数组中的下一个数,然后prev和cur再算再覆盖再取下一个,直到数组就剩一个数,返回结果。如果数组本身就是空的,那么根本不会走function,直接报错,如果数组本身只有一个值,那么计算结果就是这个值。
简单示例

Function.call和Function.apply

myFunction.call( [ thisObj [, arg1 [, arg2 [, args...]]]] )

这行代码说白了就是执行myFunction,参数是arg1,arg2,arg3…
thisObj说的高端点叫指定上下文,说白了就是解释myFunction内部的this到底指向了谁。传null或undefined时,将是JS执行环境的全局变量。浏览器中是window,其它环境(如node)则是global。
call和apply的区别是,call的参数arg1,arg2是一个一个传进去的,apply直接是一个数组。

call(obj,1,2,3)和apply(obj,[1,2,3])是一样的

prototype只有函数才有,而__proto__每个对象都有

可以读一读这个:阮一峰:Javascript继承机制的设计思想 ,挺有意思的。

大多数情况下__proto__可以理解为构造器的原型,即__proto__===constructor.prototype,但是通过 Object.create()创建的对象有可能不是, Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__ 来源简书:Kevin丶CK

又一道常见的题

var array=[];
function test() {  
    for(var i = 0; i < 4;i++){     
       var temp = function(){  
           console.log(i);  
       }  
       array.push(temp);
    }  
} 
test();
array[0]();
array[1]();
array[2]();
array[3]();

上面这一段JS代码初学者看了会以为输出是0 1 2 3,老手一看就知道输出的是4 4 4 4,具体的原因其实很简单。

首先array这个数组被填进去了四个函数,此时填进去的函数并不会被执行,也就是执行test()的时候并不会执行console.log(i)。函数执行完了,此时i的值是4,然后array[0] ()执行的时候,实际上是在执行当初填进去的那个temp函数,temp函数的功能是打印i,这个i并不是循环时的i,而是调用temp这个方法时的i。调用temp时循环已经结束了i是4,所以就会打印4。后面三个调用同理。说的再详细一点就是由于使用了var定义变量,变量会被提升,四个方法引用的都是同一个变量,如果是let定义则不会提升,每个方法一个自己的变量。

this

this一般出现在function里,表示上下文。简单来讲,谁调用的function,this就是谁(这一点和其他语言差距非常大)。如果是使用call或者apply调用function,那么传进去的第一个参数就是这个function的this。

构造函数在通过new的方式调用时,new相当于建立一个空的object,然后对function使用call调用,再把新建的这个object传进去,此时在构造函数里的this,就指向这个新建的空object。

 function person(name,age){
	this.name=name;
	this.age=age;
}

let xiaoming=new person('小明',15);
//上面这行代码相当于做了下面这两件事,但是不会完全一样,理解意思
let xiaoming={}
person.call(xiaoming,'小明',15);//person执行时,this指向了xiaoming

微服务和宏服务

macrotask(宏任务):script(整体代码), XHR回调、事件回调(鼠标键盘事件)setTimeout,
setInterval, setImmediate(node独有), I/O, UI rendering

microtask(微任务):process.nextTick(node独有), Promises.then,
Object.observe(废弃), MutationObserver

执行顺序,包括await和async的情况,建议看这个:讲的还是不错的。https://www.cnblogs.com/fangdongdemao/p/10262209.html

setTimeout(cb, ms)和setInterval(cb, ms)

setTimeout(cb, ms) 全局函数在指定的毫秒(ms)数后执行指定函数(cb)。只执行一次。

setInterval(cb, ms) 全局函数在指定的毫秒(ms)数后执行指定函数(cb)。会一直执行下去。

停止计时器则分别是clearTimeout和clearInterval,把创建时返回的定时器句柄传进去就行了。

Form表单submit时,指定显示的iframe

其实就是form的target属性的使用,如果target不是_blank或者_self等,而是自己定义的一个字符串,那么form提交以后跳转的页面将在同名的iframe里显示

<iframe name="hahaha"></iframe>
<form target="hahaha"></form>

这个form触发submit时,跳转的页面将在同名iframe里显示

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值