1.前言
经常关注大厂面试题分享的会发现,大厂非常注重js基础,苦于啃五花八门的js框架而烦恼的你不如补一补你的js基础(求放过~这里没有贬低学js框架的意思)这编文章是整理js忍者秘籍重要的知识点和加上本人的理解,看了会让你对js的了解进一个阶层!憋怀疑,本人就是一个栗子。(微笑脸)
2.函数的独特之处
函数是第一型对象,因为函数可以像对象通过字面量进行创建,赋值变量、数组,作为函数的返回值,拥有动态创建并返回赋值的属性。函数最重要的特性是它可以被调用,而通常都是以异步的方式进行调用,其原理是浏览器事件轮询是单线程的,即每个事件都是按照在队列放放置的顺序类执行的,就是FIFO(先进先出)列表,在任何情况下单线程不可能同时执行两个程序,所以必须等待当前事件结束之后才能执行另外一个事件,就是说事件执行的时间不可知所以事件处理函数调用异步。
3.函数声明
函数是使用字面量进行声明从而创建函数值的,函数字面量由四个部分组成
1.function关键词
2.可选名称
3.括号内部,一个以逗号分隔开的参数列表
4.函数体
复制代码
我知道你想问什么,没错!函数名是可选的,它只是函数的引用,匿名函数就是一个栗子。可以通过访问函数的name属性获取函数名
function ninja(){}
ninja.name //ninja
复制代码
另外值得注意的是书上说创建一个匿名函数并赋值给一个变量,这个变量并不是该函数的name,但是本人发现在支持ES6语法的chrome下获取函数name不是空而是匿名函数赋值给该变量的变量名。
var fn = function () {};
// ES5
fn.name // ""
// ES6
fn.name // "fn"
复制代码
也可以通过属性访问的方式获取形参长度,注意是形参不是实参,形参和实参的区别是:形参是函数声明时定义的参数,而实参是函数调用时传给函数的参数。下面会讲怎么获取实参。
var fn = function (a,b) {};
fn.length // 2
复制代码
4.函数调用
函数被调用的时候到底发生了什么?事实上函数被调用的方式对其函数内部代码执行有着巨大的影响。有四种不同的方式进行函数调用,每个方式都有细微的差别。
- 作为一个函数调用,是简单的形式
- 作为一个方法调用,在对象上进行调用
- 作为构造器调用,创建一个对象
- 通过apply()和call()方法进行调用
有趣的是在函数调用的时候都会传递两个隐式参数:arguments和this,所谓隐式就是这些参数不会显示在函数签名里,但是它们会传递给函数并在函数作用域内,在函数内可以进行访问和使用。
arguments
arguments是函数调用时传给函数的实际参数集合,可以用length属性获取集合的长度,但是arguments并不是真正的数组。
function fn (a,b,c){console.log(arguments.length) }
fn.length // 3
fn(6,6,) // 2
复制代码
this参数
this就是函数上下文,而this指向依赖于函数的调用方式,所以this称作调用上下文更合适。 当函数作为“函数调用“,是指区别于方法、构造器以及apply/call,this指向与widow对象
function ninja(){console.log(this)}
ninja() //window
复制代码
当函数作为“方法调用”,是指当函数被赋值给对象的一个属性,并使用引用该函数的这个属性进行调用,this指向该对象,实例如下:
var o={};
o.ninja=function(){ console.log(this)};
o.ninja(); // o
复制代码
当函数作为“构造器”进行调用时:
- 创建一个新的对象
- 将构造器函数作用域赋值给新对象(因此this指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 如果没有显式的返回值,返回新对象
function Ninja(){
this.shulk=function(){console.log(this)}
}
var Ninja1=new Ninja();
var Ninja1=new Ninja();
Ninja1.shulk() //Ninja1
Ninja2.shulk() //Ninja2
复制代码
使用apply()和call()方法进行调用 在函数调用的时候JavaScript为我们提供一种可以显式指定任何一个对象作为函数的上下文,使用apply()和call()方法可以实现这种功能
function juggle(){
var result=0;
for( var i=0;i<arguments.length;i++){
result=+arguments[i];
}
this.result=result;
}
var ninja1={},ninja2={};
juggle.apply(ninja1,[1,2,3,4])
console.log(ninja1.result) // 10
juggle.call(ninja2,5,6,7,8)
console.log(ninja2.result) // 26
复制代码
在本例中可以看出apply()方法把函数juggle上下文指定给了ninja1对象,call()方法把函数juggle上下文指定给了ninja2。常见使用apply()、call()方法是在实现函数回调的时候,比如下面实现一个简单的forEach函数:
function forEach(list,callback)(){
for(var i=0;i>list.length;i++){
callback.call(list[i],list[i],i)
}
}
var arr=['Tom','alice','jack'];
forEach(arr,function(e,i){
console.log(e)
}) //Tom,alice,jack
复制代码
apply()和call()的功能基本相同,我们该选择哪个比较好呢? 如果在变量里有很多无关的值或者是指定为字面量,使用call()则可以直接将其作为参数列表传进去,但是如果这些参数已经在一个数组里了,或者很容易将其收集到数组里,apply()是更好选择。