面试中的JavaScript之函数对象数组.md

Posted 东夋壬

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试中的JavaScript之函数对象数组.md相关的知识,希望对你有一定的参考价值。

面试中的javascript

JavaScript ( JS ) 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。虽然它是作为开发Web 页面的脚本语言而出名的,但是它也被用到了很多非浏览器环境中,例如 Node.js、 Apache CouchDB 和 Adobe Acrobat。JavaScript 是一种基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式和声明式(如函数式编程)风格。

JavaScript 的标准是 ECMAScript 。截至 2012 年,所有的现代浏览器都完整的支持 ECMAScript 5.1,旧版本的浏览器至少支持 ECMAScript 3 标准。2015年6月17日,ECMA国际组织发布了 ECMAScript 的第六版,该版本正式名称为 ECMAScript 2015,但通常被称为 ECMAScript 6 或者 ES6。

函数

声明方式

1.函数声明:有预解析

// ES5
function getSum(){}
function (){ // 匿名函数

// ES6
() => {} // 参数只有一个可以省略括号,返回值是一个表达式可以省略花括号和return

2.函数表达式(字面量):

// ES5
var sum=function(){}
// ES6
let sum=()=>{}

3.构造函数:会解析两次代码,第一次解析常规的JavaScript代码,第二次解析传入构造函数的字符串

const sum = new Function(\'a\', \'b\' , \'return a + b\')

ES5中函数调用和this

1.调用中的this指向:

function getSum() {
  console.log(this)  // this指向window
}

(function() {
  console.log(this) // 匿名函数调用,this指向window
 })()
 
 var getSum=function() {
  console.log(this) // this指向window
 }

2.对象中方法调用:

var objList = {
  name: \'methods\',
  getSum: function() {
    console.log(this) // objList对象
  }
}

3.构造器调用:

function Person() {
  console.log(this); // 构造函数调用,this指向实例对象personOne
}
var personOne = new Person();

4.间接调用:call和apply对应的第一个参数,如果不传值或者第一个值为null,undefined时this指向window

function foo() {
  console.log(this);
}
foo.apply(\'我是apply改变的this值\'); // String {"我是apply改变的this值"},指向String对象
foo.call(\'我是call改变的this值\'); // String {"我是call改变的this值"}

ES6中函数的调用和this

(() => {
  console.log(this) // window
})()

let arrowFun = () => {
  console.log(this) // window
}

let arrowObj = {
  arrFun: function() {
    (() => {
      console.log(this) // this指向的是arrowObj对象
    })()
  }
}
arrowObj.arrFun();

ES6中块级作用域和函数声明

ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。但是,浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,不会报错。

ES5环境下可以实现下面代码:

demo()  // 打印 bbb

var flag = true
if (flag) {
  function demo() {
    console.log(\'aaa\')
  }
} else {
  function demo() {
    console.log(\'bbb\')
  }
}

执行此代码时,会先将函数声明提升到顶部而并不会根据判断在下面进行声明,打印bbb是因为第一个声明被第二个声明覆盖了,实际为下面代码:

function demo() {
  console.log(\'aaa\')
}
function demo() {
  console.log(\'bbb\')
}
var flag
demo()  // 打印 bbb
flag = true
if (flag) {

} else {

}

ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。

如果改变了块级作用域内声明的函数的处理规则,显然会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6 在附录 B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式:

  • 允许在块级作用域内声明函数
  • 函数声明类似于var,即会提升到全局作用域或函数作用域的头部
  • 同时,函数声明还会提升到所在的块级作用域的头部

注意,上面三条规则只对 ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let处理。

上面代码的执行顺序会变为:

var flag
var demo
demo()  // demo is not a function

flag = true
if (flag) {
  function demo() {
    console.log(\'aaa\')
  }
} else {
  function demo() {
   console.log(\'bbb\')
  }
}

call,apply和bind

1.IE5之前不支持callapplybind是ES5出来的,callapply可以调用函数,改变this,实现继承和借用别的对象的方法;

2.call和apply:对象.call(新this对象,实参1,实参2,实参3.....) 对象.apply(新this对象,[实参1,实参2,实参3.....])

var foo = {
  name:"张三",
  logName:function(){
    console.log(this.name);
  }
}
var bar={
  name:"李四"
};
foo.logName.call(bar); // \'李四\',实质是call改变了foo的this指向为bar,并调用该函数

3.继承:

function Animal(name){   
  this.name = name;   
  this.showName = function(){   
    console.log(this.name);   
  }   
}   
function Cat(name){  
  Animal.call(this, name);  
}    
var cat = new Cat("Black Cat");   
cat.showName(); //Black Cat

4.借用数组方法:

(function(){
  Array.prototype.push.call(arguments,\'王五\');
  console.log(arguments); // [\'张三\',\'李四\',\'王五\']
})(\'张三\',\'李四\')

var arr1=[1,2,3]; 
var arr2=[4,5,6]; 
Array.prototype.push.apply(arr1,arr2); // arr1,[1, 2, 3, 4, 5, 6]

5.其他:

Math.max.apply(null,arr) // 求数组最大值

Object.prototype.toString.call({}) // "[object Object]",判断字符类型

6.bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用,不兼容IE8:

bind 是创建一个新的函数,我们必须要手动去调用。apply和call是立即执行。

const module = {
  x: 42,
  getX: function() {
    return this.x;
  }
}
const unboundGetX = module.getX;
console.log(unboundGetX()); // expected output: undefined

const boundGetX = unboundGetX.bind(module);
console.log(boundGetX()); // expected output: 42

7.call,apply和bind原生实现

Function.prototype.newCall = function(context, ...parameter) {
  if (typeof context === \'object\' || typeof context === \'function\') {
    context = context || window
  } else {
    context = Object.create(null)
  }
  context[fn] = this  
  const res =context[fn](...parameter)
  delete context.fn;
  return res
}

Function.prototype.newApply = function(context, parameter) {
  if (typeof context === \'object\' || typeof context === \'function\') {
    context = context || window
  } else {
    context = Object.create(null)
  }
  let fn = Symbol()
  context[fn] = this
  return res=context[fn](..parameter);
  delete context[fn]
  return res
}

Function.prototype.bind = function (context,...innerArgs) {
  var fn = this
  return function (...finnalyArgs) {
    return fn.call(context,...innerArgs,...finnalyArgs)
  }
}
var person = {
  name: \'Abiel\'
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
var personSayHi = sayHi.bind(person, 25)
personSayHi(\'男\') // Abiel 25 男

节流和防抖

1.节流:事件触发后每隔一段时间触发一次,可触发多次;常用作处理scroll、resize事件一段时间触发多次

let throttle = function(func, delay) {
  let timer = null;
  return ()=> {
    if (!timer) {
      timer = setTimeout(()=> {
        func.apply(this, arguments);
        timer = null;
      }, delay);
    }
  };
};
function handle() {
  console.log(Math.random());
}
window.addEventListener("scroll", throttle(handle, 1000));

throttle(fun, delay, immediate) {
  let flag = false;
  return (...args) => {
    if (!flag) {
      flag = true;
      setTimeout(() => {
        fun.apply(this, args);
        flag = false;
      }, delay);
    }
  };
}

2.防抖:事件触发动作完成后一段时间触发一次;scroll,resize事件触发完后一段时间触发

function debounce(fn, wait) {
  var timeout = null;
  return function() {
    if (timeout !== null) clearTimeout(timeout); // 如果多次触发将上次记录延迟清除掉
    timeout = setTimeout(()=> {
      fn.apply(this, arguments);
      timeout = null;
    }, wait);
  };
}
function handle() {
  console.log(Math.random());
}
window.addEventListener("scroll", debounce(handle, 1000));

debounce(fun, delay, immediate) {
  let timer = null;
  return (...args) => {
    if (timer) {
      clearTimeout(timer);
    } JavaScript数组(Array)类型之迭代方法

JavaScript基础--------三座大山(前端面试必考)

前端面试 JavaScript— 什么是高阶函数?数组中的高阶函数有哪些?

前端面试 JavaScript— 什么是高阶函数?数组中的高阶函数有哪些?

前端面试知识点梳理之五——javascript面向对象

前端面试 JavaScript— 函数的arguments为什么不是数组?如何转化成数组?