面试中的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之前不支持call
和apply
,bind
是ES5出来的,call
和apply
可以调用函数,改变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— 什么是高阶函数?数组中的高阶函数有哪些?