精华内容
下载资源
问答
  • 前端es6面试题

    2020-11-30 20:42:07
    1.声明变量: let 不允许在相同作用域内,重复声明同一个变量。 const 声明一个只读的常量。一旦声明,常量的值就不能改变。 2.变量解构赋值: 默认值 默认值可以引用解构赋值的其他变量,但该变量必须已经声明。...

    1.声明变量:

    let 不允许在相同作用域内,重复声明同一个变量。
    const 声明一个只读的常量。一旦声明,常量的值就不能改变。

    2.变量解构赋值:

    默认值 默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
    对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
    解构失败,变量的值等于undefined
    字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象
    函数参数的解构也可以使用默认值。

    3.箭头函数:

    箭头函数的分类:1.单语句,单参数 2.单语句,多参数 3.多语句,多参数.

    4.Symbol

    S6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)

    5.forEach()

    forEach(): 没有返回值,本质上等同于 for 循环,对每一项执行 function 函数。即map是返回一个新数组,原数组不变,forEach 是改变原数组。

    6.Rest:

    Rest 参数接受函数的多余参数,组成一个数组,放在形参的rest参数只能写在最后,前面用…标识Spread操作符(…),也称作展开操作符,作用是将可迭代的(Iterable)对象进行展开。

    7.模板字符串:

    模板字符串使用反引号 () 来代替普通字符串中的用双引号和单引号。模板字符串可以包含特定语法(${})的占位符。占位符中的表达式和周围的文本会一起传递给一个默认函数,该函数负责将所有的部分连接起来,如果一个模板字符串由表达式开头,则该字符串被称为带标签的模板字符串,该表达式通常是一个函数,它会在模板字符串处理后被调用,在输出最终结果前,你都可以通过该函数来对模板字符串进行操作处理。在模版字符串内使用反引号(`)时,需要在它前面加转义符(\)。

    8.class:

    S6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

    9.class继承:

    Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后对其加工,加上子类自己的实例属性和方法,如果不调用super方法,子类就得不到this对象。

    10.super关键字

    super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同.
    第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
    第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

    11.类的prototype属性和_proto_属性

    子类的_prop_属性,表示构造函数的继承,总是指向父类。
    子类prototype属性的_prop_属性,表示方法的继承,总是指向父类的prototype属性。

    12.Typeof

    typeof运算符用于判断对象的类型,但是对于一些创建的对象,它们都会返回’object’,有时我们需要判断该实例是否为某个对象的实例,那么这个时候需要用到instanceof运算符,后续记录instanceof运算符的相关用法。

    展开全文
  • 前端ES6面试题

    万次阅读 多人点赞 2019-06-15 14:43:04
    1、ES5、ES6和ES2015有什么区别? ES2015特指在2015年发布的新一代JS语言标准,ES6泛指下一代JS语言标准,包含ES2015、ES2016、ES2017、ES2018等。现阶段在绝大部分场景下,ES2015默认等同ES6。ES5泛指上一代语言...

    1、ES5、ES6和ES2015有什么区别?

    ES2015特指在2015年发布的新一代JS语言标准,ES6泛指下一代JS语言标准,包含ES2015ES2016ES2017ES2018等。现阶段在绝大部分场景下,ES2015默认等同ES6ES5泛指上一代语言标准。ES2015可以理解为ES5ES6的时间分界线

    2、babel是什么,有什么作用?

    babel是一个 ES6 转码器,可以将 ES6 代码转为 ES5 代码,以便兼容那些还没支持ES6的平台

    3、let有什么用,有了var为什么还要用let?

    ES6之前,声明变量只能用varvar方式声明变量其实是很不合理的,准确的说,是因为ES5里面没有块级作用域是很不合理的。没有块级作用域回来带很多难以理解的问题,比如for循环var变量泄露,变量覆盖等问题。let声明的变量拥有自己的块级作用域,且修复了var声明变量带来的变量提升问题。

    4、举一些ES6对String字符串类型做的常用升级优化?

    优化部分

    ES6新增了字符串模板,在拼接大段字符串时,用反斜杠()`取代以往的字符串相加的形式,能保留所有空格和换行,使得字符串拼接看起来更加直观,更加优雅

    升级部分

    ES6String原型上新增了includes()方法,用于取代传统的只能用indexOf查找包含字符的方法(indexOf返回-1表示没查到不如includes方法返回false更明确,语义更清晰), 此外还新增了startsWith(), endsWith(), padStart(),padEnd(),repeat()等方法,可方便的用于查找,补全字符串

    5、举一些ES6对Array数组类型做的常用升级优化

    优化部分

    • 数组解构赋值。ES6可以直接以let [a,b,c] = [1,2,3]形式进行变量赋值,在声明较多变量时,不用再写很多let(var),且映射关系清晰,且支持赋默认值
    • 扩展运算符。ES6新增的扩展运算符(...)(重要),可以轻松的实现数组和松散序列的相互转化,可以取代arguments对象和apply方法,轻松获取未知参数个数情况下的参数集合。(尤其是在ES5中,arguments并不是一个真正的数组,而是一个类数组的对象,但是扩展运算符的逆运算却可以返回一个真正的数组)。扩展运算符还可以轻松方便的实现数组的复制和解构赋值(let a = [2,3,4]; let b = [...a]

    升级部分

    ES6Array原型上新增了find()方法,用于取代传统的只能用indexOf查找包含数组项目的方法,且修复了indexOf查找不到NaN的bug([NaN].indexOf(NaN) === -1).此外还新增了copyWithin(),includes(), fill(),flat()等方法,可方便的用于字符串的查找,补全,转换等

    6、举一些ES6对Number数字类型做的常用升级优化

    优化部分

    ES6在Number原型上新增了isFinite(), isNaN()方法,用来取代传统的全局isFinite(), isNaN()方法检测数值是否有限、是否是NaNES5isFinite(), isNaN()方法都会先将非数值类型的参数转化为Number类型再做判断,这其实是不合理的,最造成isNaN('NaN') === true的奇怪行为--'NaN'是一个字符串,但是isNaN却说这就是NaN。而Number.isFinite()Number.isNaN()则不会有此类问题(Number.isNaN('NaN') === false)。(isFinite()同上)

    升级部分

    ES6Math对象上新增了Math.cbrt()trunc()hypot()等等较多的科学计数法运算方法,可以更加全面的进行立方根、求和立方根等等科学计算

    7、举一些ES6对Object类型做的常用升级优化?(重要)

    优化部分

    对象属性变量式声明。ES6可以直接以变量形式声明对象属性或者方法,。比传统的键值对形式声明更加简洁,更加方便,语义更加清晰

    let [apple, orange] = ['red appe', 'yellow orange'];
    let myFruits = {apple, orange};    // let myFruits = {apple: 'red appe', orange: 'yellow orange'};
    

    尤其在对象解构赋值(见优化部分b.)或者模块输出变量时,这种写法的好处体现的最为明显

    let {keys, values, entries} = Object;
    let MyOwnMethods = {keys, values, entries}; // let MyOwnMethods = {keys: keys, values: values, entries: entries}
    

    可以看到属性变量式声明属性看起来更加简洁明了。方法也可以采用简洁写法

    let es5Fun = {
        method: function(){}
    }; 
    let es6Fun = {
        method(){}
    }
    

    对象的解构赋值。 ES6对象也可以像数组解构赋值那样,进行变量的解构赋值

    let {apple, orange} = {apple: 'red appe', orange: 'yellow orange'};
    

    对象的扩展运算符(...)。 ES6对象的扩展运算符和数组扩展运算符用法本质上差别不大,毕竟数组也就是特殊的对象。对象的扩展运算符一个最常用也最好用的用处就在于可以轻松的取出一个目标对象内部全部或者部分的可遍历属性,从而进行对象的合并和分解

    let {apple, orange, ...otherFruits} = {apple: 'red apple', orange: 'yellow orange', grape: 'purple grape', peach: 'sweet peach'}; 
    // otherFruits  {grape: 'purple grape', peach: 'sweet peach'}
    // 注意: 对象的扩展运算符用在解构赋值时,扩展运算符只能用在最有一个参数(otherFruits后面不能再跟其他参数)
    let moreFruits = {watermelon: 'nice watermelon'};
    let allFruits = {apple, orange, ...otherFruits, ...moreFruits};
    

    super 关键字。ES6Class类里新增了类似this的关键字super。同this总是指向当前函数所在的对象不同,super关键字总是指向当前函数所在对象的原型对象

    升级部分

    ES6Object原型上新增了is()方法,做两个目标对象的相等比较,用来完善'==='方法。'==='方法中NaN === NaN //false其实是不合理的,Object.is修复了这个小bug(Object.is(NaN, NaN) // true)

    ES6Object原型上新增了assign()方法,用于对象新增属性或者多个对象合并

    const target = { a: 1 };
    const source1 = { b: 2 };
    const source2 = { c: 3 };
    Object.assign(target, source1, source2);
    target // {a:1, b:2, c:3}
    

    注意: assign合并的对象target只能合并source1、source2中的自身属性,并不会合并source1source2中的继承属性,也不会合并不可枚举的属性,且无法正确复制get和set属性(会直接执行get/set函数,取return的值)

    • ES6Object原型上新增了getOwnPropertyDescriptors()方法,此方法增强了ES5getOwnPropertyDescriptor()方法,可以获取指定对象所有自身属性的描述对象。结合defineProperties()方法,可以完美复制对象,包括复制getset属性
    • ES6Object原型上新增了getPrototypeOf()setPrototypeOf()方法,用来获取或设置当前对象的prototype对象。这个方法存在的意义在于,ES5中获取设置prototype对像是通过__proto__属性来实现的,然而__proto__属性并不是ES规范中的明文规定的属性,只是浏览器各大产商“私自”加上去的属性,只不过因为适用范围广而被默认使用了,再非浏览器环境中并不一定就可以使用,所以为了稳妥起见,获取或设置当前对象的prototype对象时,都应该采用ES6新增的标准用法
    • ES6Object原型上还新增了Object.keys()Object.values()Object.entries()方法,用来获取对象的所有键、所有值和所有键值对数组

    8、举一些ES6对Function函数类型做的常用升级优化?

    优化部分

    箭头函数(核心)。箭头函数是ES6核心的升级项之一,箭头函数里没有自己的this,这改变了以往JS函数中最让人难以理解的this运行机制。主要优化点

    • 箭头函数内的this指向的是函数定义时所在的对象,而不是函数执行时所在的对象。ES5函数里的this总是指向函数执行时所在的对象,这使得在很多情况下this的指向变得很难理解,尤其是非严格模式情况下,this有时候会指向全局对象,这甚至也可以归结为语言层面的bug之一。ES6的箭头函数优化了这一点,它的内部没有自己的this,这也就导致了this总是指向上一层的this,如果上一层还是箭头函数,则继续向上指,直到指向到有自己this的函数为止,并作为自己的this
    • 箭头函数不能用作构造函数,因为它没有自己的this,无法实例化
    • 也是因为箭头函数没有自己的this,所以箭头函数 内也不存在arguments对象。(可以用扩展运算符代替)
    • 函数默认赋值。ES6之前,函数的形参是无法给默认值得,只能在函数内部通过变通方法实现。ES6以更简洁更明确的方式进行函数默认赋值
    function es6Fuc (x, y = 'default') {
        console.log(x, y);
    }
    es6Fuc(4) // 4, default
    

    升级部分

    ES6新增了双冒号运算符,用来取代以往的bindcall,和apply。(浏览器暂不支持,Babel已经支持转码)

    foo::bar;
    // 等同于
    bar.bind(foo);
    
    foo::bar(...arguments);
    // 等同于
    bar.apply(foo, arguments);
    

    9、Symbol是什么,有什么作用?

    SymbolES6引入的第七种原始数据类型(说法不准确,应该是第七种数据类型,Object不是原始数据类型之一,已更正),所有Symbol()生成的值都是独一无二的,可以从根本上解决对象属性太多导致属性名冲突覆盖的问题。对象中Symbol()属性不能被for...in遍历,但是也不是私有属性

    10、Set是什么,有什么作用?

    SetES6引入的一种类似Array的新的数据结构,Set实例的成员类似于数组item成员,区别是Set实例的成员都是唯一,不重复的。这个特性可以轻松地实现数组去重

    11、Map是什么,有什么作用?

    MapES6引入的一种类似Object的新的数据结构,Map可以理解为是Object的超集,打破了以传统键值对形式定义对象,对象的key不再局限于字符串,也可以是Object。可以更加全面的描述对象的属性

    12、Proxy是什么,有什么作用?

    ProxyES6新增的一个构造函数,可以理解为JS语言的一个代理,用来改变JS默认的一些语言行为,包括拦截默认的get/set等底层方法,使得JS的使用自由度更高,可以最大限度的满足开发者的需求。比如通过拦截对象的get/set方法,可以轻松地定制自己想要的key或者value。下面的例子可以看到,随便定义一个myOwnObjkey,都可以变成自己想要的函数`

    function createMyOwnObj() {
    	//想把所有的key都变成函数,或者Promise,或者anything
    	return new Proxy({}, {
    		get(target, propKey, receiver) {
    			return new Promise((resolve, reject) => {
    				setTimeout(() => {
    					let randomBoolean = Math.random() > 0.5;
    					let Message;
    					if (randomBoolean) {
    						Message = `你的${propKey}运气不错,成功了`;
    						resolve(Message);
    					} else {
    						Message = `你的${propKey}运气不行,失败了`;
    						reject(Message);
    					}
    				}, 1000);
    			});
    		}
    	});
    }
    
    let myOwnObj = createMyOwnObj();
    
    myOwnObj.hahaha.then(result => {
    	console.log(result) //你的hahaha运气不错,成功了
    }).catch(error => {
    	console.log(error) //你的hahaha运气不行,失败了
    })
    
    myOwnObj.wuwuwu.then(result => {
    	console.log(result) //你的wuwuwu运气不错,成功了
    }).catch(error => {
    	console.log(error) //你的wuwuwu运气不行,失败了
    })
    
    

    13、Reflect是什么,有什么作用?

    ReflectES6引入的一个新的对象,他的主要作用有两点,一是将原生的一些零散分布在ObjectFunction或者全局函数里的方法(如applydeletegetset等等),统一整合到Reflect上,这样可以更加方便更加统一的管理一些原生API。其次就是因为Proxy可以改写默认的原生API,如果一旦原生API别改写可能就找不到了,所以Reflect也可以起到备份原生API的作用,使得即使原生API被改写了之后,也可以在被改写之后的API用上默认的API

    14、Promise是什么,有什么作用?

    PromiseES6引入的一个新的对象,他的主要作用是用来解决JS异步机制里,回调机制产生的“回调地狱”。它并不是什么突破性的API,只是封装了异步回调形式,使得异步回调可以写的更加优雅,可读性更高,而且可以链式调用

    15、Iterator是什么,有什么作用?(重要)

    • IteratorES6中一个很重要概念,它并不是对象,也不是任何一种数据类型。因为ES6新增了SetMap类型,他们和ArrayObject类型很像,ArrayObject都是可以遍历的,但是SetMap都不能用for循环遍历,解决这个问题有两种方案,一种是为SetMap单独新增一个用来遍历的API,另一种是为SetMapArrayObject新增一个统一的遍历API,显然,第二种更好,ES6也就顺其自然的需要一种设计标准,来统一所有可遍历类型的遍历方式。Iterator正是这样一种标准。或者说是一种规范理念
    • 就好像JavaScriptECMAScript标准的一种具体实现一样,Iterator标准的具体实现是Iterator遍历器。Iterator标准规定,所有部署了key值为[Symbol.iterator],且[Symbol.iterator]value是标准的Iterator接口函数(标准的Iterator接口函数: 该函数必须返回一个对象,且对象中包含next方法,且执行next()能返回包含value/done属性的Iterator对象)的对象,都称之为可遍历对象,next()后返回的Iterator对象也就是Iterator遍历器
    //obj就是可遍历的,因为它遵循了Iterator标准,且包含[Symbol.iterator]方法,方法函数也符合标准的Iterator接口规范。
    //obj.[Symbol.iterator]() 就是Iterator遍历器
    let obj = {
      data: [ 'hello', 'world' ],
      [Symbol.iterator]() {
        const self = this;
        let index = 0;
        return {
          next() {
            if (index < self.data.length) {
              return {
                value: self.data[index++],
                done: false
              };
            } else {
              return { value: undefined, done: true };
            }
          }
        };
      }
    };
    
    

    ES6SetMapArrayString都加上了[Symbol.iterator]方法,且[Symbol.iterator]方法函数也符合标准的Iterator接口规范,所以SetMapArrayString默认都是可以遍历的

    //Array
    let array = ['red', 'green', 'blue'];
    array[Symbol.iterator]() //Iterator遍历器
    array[Symbol.iterator]().next() //{value: "red", done: false}
    
    //String
    let string = '1122334455';
    string[Symbol.iterator]() //Iterator遍历器
    string[Symbol.iterator]().next() //{value: "1", done: false}
    
    //set
    let set = new Set(['red', 'green', 'blue']);
    set[Symbol.iterator]() //Iterator遍历器
    set[Symbol.iterator]().next() //{value: "red", done: false}
    
    //Map
    let map = new Map();
    let obj= {map: 'map'};
    map.set(obj, 'mapValue');
    map[Symbol.iterator]().next()  {value: Array(2), done: false}
    
    
    

    16、for…in 和for…of有什么区别?

    如果看到问题十六,那么就很好回答。问题十六提到了ES6统一了遍历标准,制定了可遍历对象,那么用什么方法去遍历呢?答案就是用for...of。ES6规定,有所部署了载了Iterator接口的对象(可遍历对象)都可以通过for...of去遍历,而for..in仅仅可以遍历对象

    • 这也就意味着,数组也可以用for...of遍历,这极大地方便了数组的取值,且避免了很多程序用for..in去遍历数组的恶习

    17、Generator函数是什么,有什么作用?

    • 如果说JavaScriptECMAScript标准的一种具体实现、Iterator遍历器是Iterator的具体实现,那么Generator函数可以说是Iterator接口的具体实现方式。
    • 执行Generator函数会返回一个遍历器对象,每一次Generator函数里面的yield都相当一次遍历器对象的next()方法,并且可以通过next(value)方法传入自定义的value,来改变Generator函数的行为。
    • Generator函数可以通过配合Thunk 函数更轻松更优雅的实现异步编程和控制流管理。

    18、async函数是什么,有什么作用?

    async函数可以理解为内置自动执行器的Generator函数语法糖,它配合ES6Promise近乎完美的实现了异步编程解决方案

    19、Class、extends是什么,有什么作用?

    ES6class可以看作只是一个ES5生成实例对象的构造函数的语法糖。它参考了java语言,定义了一个类的概念,让对象原型写法更加清晰,对象实例化更像是一种面向对象编程。Class类可以通过extends实现继承。它和ES5构造函数的不同点

    类的内部定义的所有方法,都是不可枚举的

    ///ES5
    function ES5Fun (x, y) {
    	this.x = x;
    	this.y = y;
    }
    ES5Fun.prototype.toString = function () {
    	 return '(' + this.x + ', ' + this.y + ')';
    }
    var p = new ES5Fun(1, 3);
    p.toString();
    Object.keys(ES5Fun.prototype); //['toString']
    
    //ES6
    class ES6Fun {
    	constructor (x, y) {
    		this.x = x;
    		this.y = y;
    	}
    	toString () {
    		return '(' + this.x + ', ' + this.y + ')';
    	}
    }
    
    Object.keys(ES6Fun.prototype); //[]
    
    
    • ES6class类必须用new命令操作,而ES5的构造函数不用new也可以执行。
    • ES6class类不存在变量提升,必须先定义class之后才能实例化,不像ES5中可以将构造函数写在实例化之后。
    • ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this

    20、module、export、import是什么,有什么作用?

    • moduleexportimportES6用来统一前端模块化方案的设计思路和实现方案。exportimport的出现统一了前端模块化的实现方案,整合规范了浏览器/服务端的模块化方法,用来取代传统的AMD/CMDrequireJSseaJScommondJS等等一系列前端模块不同的实现方案,使前端模块化更加统一规范,JS也能更加能实现大型的应用程序开发。
    • import引入的模块是静态加载(编译阶段加载)而不是动态加载(运行时加载)。
    • import引入export导出的接口值是动态绑定关系,即通过该接口,可以取到模块内部实时的值

    21、日常前端代码开发中,有哪些值得用ES6去改进的编程优化或者规范?

    • 常用箭头函数来取代var self = this;的做法。
    • 常用let取代var命令。
    • 常用数组/对象的结构赋值来命名变量,结构更清晰,语义更明确,可读性更好。
    • 在长字符串多变量组合场合,用模板字符串来取代字符串累加,能取得更好地效果和阅读体验。
    • Class类取代传统的构造函数,来生成实例化对象。
    • 在大型应用开发中,要保持module模块化开发思维,分清模块之间的关系,常用importexport方法。

    22、ES6的了解

    新增模板字符串(为JavaScript提供了简单的字符串插值功能)、箭头函数(操作符左边为输入的参数,而右边则是进行的操作以及返回的值Inputs=>outputs。)、for-of(用来遍历数据—例如数组中的值。)arguments对象可被不定参数和默认参数完美代替。ES6将promise对象纳入规范,提供了原生的Promise对象。增加了let和const命令,用来声明变量。增加了块级作用域。let命令实际上就增加了块级作用域。ES6规定,var命令和function命令声明的全局变量,属于全局对象的属性;let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。。还有就是引入module模块的概念

    23、说说你对Promise的理解

    • 依照 Promise/A+ 的定义,Promise 有四种状态:
      • pending: 初始状态, 非 fulfilled 或 rejected.
      • fulfilled: 成功的操作.
      • rejected: 失败的操作.
      • settled: Promise已被fulfilled或rejected,且不是pending
    • 另外, fulfilled 与 rejected 一起合称 settled
    • Promise 对象用来进行延迟(deferred) 和异步(asynchronous ) 计算

    24、Promise 的构造函数

    • 构造一个 Promise,最基本的用法如下:
    var promise = new Promise(function(resolve, reject) {
    
            if (...) {  // succeed
    
                resolve(result);
    
            } else {   // fails
    
                reject(Error(errMessage));
    
            }
        });
    
    
    • Promise 实例拥有 then 方法(具有 then 方法的对象,通常被称为thenable)。它的使用方法如下:
    promise.then(onFulfilled, onRejected)
    
    
    • 接收两个函数作为参数,一个在 fulfilled 的时候被调用,一个在rejected的时候被调用,接收参数就是 future,onFulfilled 对应 resolve, onRejected 对应 reject

    什么是 Promise ?

    • Promise 就是一个对象,用来表示并传递异步操作的最终结果
    • Promise 最主要的交互方式:将回调函数传入 then 方法来获得最终结果或出错原因
    • Promise 代码书写上的表现:以“链式调用”代替回调函数层层嵌套(回调地狱)

    解构赋值:
    只要判定了等号两边的模式相同(解构),左边的变量就会被赋予对应的值(赋值),以前一次给一个变量赋值,解构赋值可以一次性给多个变量进行赋值。

    25、谈一谈你了解ECMAScript6的新特性?

    • 块级作用区域 let a = 1;
    • 可定义常量 const PI = 3.141592654;
    • 变量解构赋值 var [a, b, c] = [1, 2, 3];
    • 字符串的扩展(模板字符串) var sum =${a + b};
    • 数组的扩展(转换数组类型) Array.from($('li'));
    • 函数的扩展(扩展运算符) [1, 2].push(...[3, 4, 5]);
    • 对象的扩展(同值相等算法) Object.is(NaN, NaN);
    • 新增数据类型(Symbol) let uid = Symbol('uid');
    • 新增数据结构(Map) let set = new Set([1, 2, 2, 3]);
    • for…of循环 for(let val of arr){};
    • Promise对象 var promise = new Promise(func);
    • Generator函数 function* foo(x){yield x; return x*x;}
    • 引入Class(类) class Foo {}
    • 引入模块体系 export default func;
    • 引入async函数[ES7]
    async function asyncPrint(value, ms) {
          await timeout(ms);
          console.log(value)
         }
         
    
    

    26、Object.is() 与原来的比较操作符 = 的区别?

    • == 相等运算符,比较时会自动进行数据类型转换
    • === 严格相等运算符,比较时不进行隐式类型转换
    • Object.is 同值相等算法,在 === 基础上对 0 和 NaN 特别处理
    +0 === -0 //true
    NaN === NaN // false
    
    Object.is(+0, -0) // false
    Object.is(NaN, NaN) // true
    
    

    27、什么是 Babel

    • Babel 是一个 JS 编译器,自带一组 ES6 语法转化器,用于转化 JS 代码。
      这些转化器让开发者提前使用最新的 JS语法(ES6/ES7),而不用等浏览器全部兼容。
    • Babel 默认只转换新的 JS 句法(syntax),而不转换新的API。
    展开全文
  • ES6前端面试题整理

    千次阅读 2021-03-18 20:52:12
    ES6前端面试题整理,es5和es6的区别, ES6新增的一些特性,箭头函数,模板字符串,解构赋值,... 展开运算符,修饰器 @,Symbol,Proxy代理,使用箭头函数应注意什么?Set、Map的区别?setTimeout、Promise、Async/Await 的...

    1、说说你了解的ES6新增特性

    let声明变量和const声明常量,两个都有块级作用域

    ES5中是没有块级作用域的,并且var有变量提升,在let中,使用的变量一定要进行声明

    箭头函数

    ES6中的函数定义不再使用关键字function(),而是利用了()=>来进行定义

    特点:箭头函数中的this始终指向箭头函数定义时的离this最近的一个函数,如果没有最近的函数就指向window。

    模板字符串

    模板字符串是增强版的字符串,用反引号(`)标识,可以当作普通字符串使用,也可以用来定义多行字符串

    var name = '丁香医生';
    var desc = '丁香医生是面向大众的科普性健康类网站';
    var html = function(name, desc){
      var tpl = '公司名:' + name + '\n'+
          '简介:'+ desc;
      return tpl;
    }
    而现在:
    
    var html = `公司名:${name}
      简介:${desc}`;
    

    解构赋值

    ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值

    for of循环

    for…of循环可以遍历数组、Set和Map结构、某些类似数组的对象、对象,以及字符串

    应用场景

    //arguments是个类数组对象,通常先要将其转换为数组才能遍历,但for...of可以直接遍历
    function sum() {
        let sum = 0
        for(let num of arguments){
            sum += num
        }
        console.log(sum);        //15
    }
    
    sum(1,2,3,4,5)
    //遍历字符串
    let name = 'Asher';
    for (let char of name){
        console.log(char);         //A s h e r
    }
    //遍历nodelists,效果如下图
    <style type="text/css">
        .completed {
            text-decoration: line-through;
        }
    </style>
    <body>
        <ul>
            <li>yoga</li>
            <li>boxing</li>
            <li>press</li>
        </ul>
    
        <script type="text/javascript">
            const lis = document.querySelectorAll('li');
            for(let li of lis){
                li.addEventListener('click',function(){
                    this.classList.toggle('completed');
                })
            }
        </script>
    </body>
    

    import、export导入导出

    ES6标准中,Js原生支持模块(module)。将JS代码分割成不同功能的小块进行模块化,将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入的方式可以在其他地方使用

    set数据结构

    Set数据结构,类似数组。所有的数据都是唯一的,没有重复的值。它本身是一个构造函数

    应用

    1、数组去重
    
    let arr = [3, 5, 2, 2, 5, 5];
    let unique = [...new Set(arr)];
    // [3, 5, 2]
    
    2、并集(Union)、交集(Intersect)和差集(Difference)
    
    let a = new Set([1, 2, 3]);
    let b = new Set([4, 3, 2]);
    
    // 并集
    let union = new Set([...a, ...b]);
    // Set {1, 2, 3, 4}
    
    // 交集
    let intersect = new Set([...a].filter(x => b.has(x)));
    // set {2, 3}
    
    // 差集
    let difference = new Set([...a].filter(x => !b.has(x)));
    // Set {1}
    

    … 展开运算符

    可以将数组或对象里面的值展开;还可以将多个值收集为一个变量

    应用

    1. 合并数组
    
    arr1.push(...arr2) // 将arr2 追加到数组的末尾
    arr1.unshift(...arr2) // 将arr2 追加到数组的开头
    
    2. 拷贝数组
    
    var arr = [1,2,3];
    var arr2 = [...arr]; // 和arr.slice()差不多
    arr2.push(4)
    //记住:数组中的对象依然是引用值,所以不是任何东西都“拷贝”过去了。
    
    3.将arguments或者NodeList转换为Array
    
    
    var myFn = function(...args) {
    // ...
    }
    
    4. 使用Math函数
    
    let numbers = [9, 4, 7, 1];
    Math.min(...numbers); // 1
    //相当于Math.min.apply(numbers);
    

    修饰器 @

    decorator是一个函数,用来修改类甚至于是方法的行为。修饰器本质就是编译时执行的函数

    class 类的继承

    ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念

    class Person
      {  
            constructor(name, age) {
              this.name = name;
              this.age = age;
            }
     
            sayName(){
                console.log("the name is:"+this.name);
            }
      }
     
      class Worker extends Person{
        constructor(name, age,job) {
             super(name, age);
              this.job = job;
        }
        sayJob(){
          console.log("the job is:"+this.job)
        }
      }
     
      var worker = new Worker('tcy',20,'teacher');
      worker.sayJob();//the job is:teacher
      worker.sayName();//the name is:tcy
    

    async、await

    使用 async/await, 搭配promise,可以通过编写形似同步的代码来处理异步流程,提高代码的简洁性和可读性

    async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成

    // 2s 之后返回双倍的值
    function doubleAfter2seconds(num) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(2 * num)
            }, 2000);
        })
    }
    
    async function testResult () {
        let first = await doubleAfter2seconds(10);
        let second = await doubleAfter2seconds(20);    
        console.log(first + second);
    }
    

    promise

    Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理、强大

    new Promise((resolve,reject)=>{
    if('成功') resolve(1);
    if('失败') reject(0);
    }).then(()=>{
        console.log("成功");
    }).catch(()=>{
        console.log("失败");
    })
    

    它有三种状态,分别是pending-进行中、resolved-已完成、rejected-已失败。

    Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject。对于已经实例化过的 promise 对象可以调用 promise.then() 方法,传递 resolve 和 reject 方法作为回调。then()方法接收两个参数:onResolve和onReject,分别代表当前 promise 对象在成功或失败时。

    var promise = new Promise((resolve, reject) => {
        var success = true;
        if (success) {
            resolve('成功');
        } else {
            reject('失败');
        }
    }).then(
        (data) => { console.log(data)},
        (err) => { console.log(err)}
    )
    

    promise的执行过程

    setTimeout(function() {
        console.log(0);
    }, 0);
    var promise = new Promise((resolve, reject) => {
        console.log(1);
        setTimeout(function () {
            var success = true;
            if (success) {
                resolve('成功');
            } else {
                reject('失败');
            }
        },2000);
    }).then(
        (data) => { console.log(data)},
        (data) => { console.log(data)}
    );
    console.log(promise);	//<pending> 进行中
    setTimeout(function () {
        console.log(promise);	//<resolved> 已完成
    },2500);
    console.log(2);
     
    //1
    //Promise {<pending>}
    //2
    //0
    //成功
    //Promise {<resolved>: undefined}
    

    Symbol

    Symbol是一种基本类型。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的

    let s1 = Symbol()
    let s2 = Symbol('another symbol')
    let s3 = Symbol('another symbol')
    
    s1 === s2 // false
    s2 === s3 // false
    //symbol是唯一的
    

    应用场景1:使用Symbol来作为对象属性名(key)

    在这之前,我们通常定义或访问对象的属性时都是使用字符串,比如下面的代码:
    
    let obj = {
      abc: 123,
      "hello": "world"
    }
    
    obj["abc"] // 123
    obj["hello"] // 'world'
    而现在,Symbol可同样用于对象属性的定义和访问:
    
    const PROP_NAME = Symbol()
    const PROP_AGE = Symbol()
    
    let obj = {
      [PROP_NAME]: "一斤代码"
    }
    obj[PROP_AGE] = 18
    
    obj[PROP_NAME] // '一斤代码'
    obj[PROP_AGE] // 18
    
    
    随之而来的是另一个非常值得注意的问题:就是当使用了Symbol作为对象的属性key后,在对该对象进行key的枚举时,会有什么不同?在实际应用中,我们经常会需要使用Object.keys()或者for...in来枚举对象的属性名,那在这方面,Symbol类型的key表现的会有什么不同之处呢?来看以下示例代码:
    
    let obj = {
       [Symbol('name')]: '一斤代码',
       age: 18,
       title: 'Engineer'
    }
    
    Object.keys(obj)   // ['age', 'title']
    
    for (let p in obj) {
       console.log(p)   // 分别会输出:'age' 和 'title'
    }
    
    Object.getOwnPropertyNames(obj)   // ['age', 'title']
    由上代码可知,Symbol类型的key是不能通过Object.keys()或者for...in来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。
    
    也正因为这样一个特性,当使用JSON.stringify()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外:
    
    JSON.stringify(obj)  // {"age":18,"title":"Engineer"}
    我们可以利用这一特点来更好的设计我们的数据对象,让“对内操作”和“对外选择性输出”变得更加优雅。
    
    然而,这样的话,我们就没办法获取以Symbol方式定义的对象属性了么?非也。还是会有一些专门针对Symbol的API,比如:
    
    // 使用Object的API
    Object.getOwnPropertySymbols(obj) // [Symbol(name)]
    
    // 使用新增的反射API
    Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']
    

    应用场景2:使用Symbol来替代常量

    先来看一下下面的代码,是不是在你的代码里经常会出现?
    
    const TYPE_AUDIO = 'AUDIO'
    const TYPE_VIDEO = 'VIDEO'
    const TYPE_IMAGE = 'IMAGE'
    
    function handleFileResource(resource) {
      switch(resource.type) {
        case TYPE_AUDIO:
          playAudio(resource)
          break
        case TYPE_VIDEO:
          playVideo(resource)
          break
        case TYPE_IMAGE:
          previewImage(resource)
          break
        default:
          throw new Error('Unknown type of resource')
      }
    }
    如上面的代码中那样,我们经常定义一组常量来代表一种业务逻辑下的几个不同类型,我们通常希望这几个常量之间是唯一的关系,为了保证这一点,我们需要为常量赋一个唯一的值(比如这里的'AUDIO'、'VIDEO'、 'IMAGE'),常量少的时候还算好,但是常量一多,你可能还得花点脑子好好为他们取个好点的名字。
    
    现在有了Symbol,我们大可不必这么麻烦了:
    
    const TYPE_AUDIO = Symbol()
    const TYPE_VIDEO = Symbol()
    const TYPE_IMAGE = Symbol()
    这样定义,直接就保证了三个常量的值是唯一的了!是不是挺方便的呢。
    

    应用场景3:使用Symbol定义类的私有属性/方法

    我们知道在JavaScript中,是没有如Java等面向对象语言的访问控制关键字private的,类上所有定义的属性或方法都是可公开访问的。因此这对我们进行API的设计时造成了一些困扰。
    
    而有了Symbol以及模块化机制,类的私有属性和方法才变成可能。例如:
    
    在文件 a.js中
    const PASSWORD = Symbol()
    
    class Login {
      constructor(username, password) {
        this.username = username
        this[PASSWORD] = password
      }
    
      checkPassword(pwd) {
          return this[PASSWORD] === pwd
      }
    }
    
    export default Login
    在文件 b.js 中
    import Login from './a'
    
    const login = new Login('admin', '123456')
    
    login.checkPassword('123456')  // true
    
    login.PASSWORD  // oh!no!
    login[PASSWORD] // oh!no!
    login["PASSWORD"] // oh!no!
    由于Symbol常量PASSWORD被定义在a.js所在的模块中,外面的模块获取不到这个Symbol,也不可能再创建一个一模一样的Symbol出来(因为Symbol是唯一的),因此这个PASSWORD的Symbol只能被限制在a.js内部使用,所以使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。
    

    Proxy代理

    使用代理(Proxy)监听对象的操作,然后可以做一些相应事情

    ES6原生提供了Proxy构造函数,用来生成Proxy实例。
    
    var proxy = new Proxy(target, handler);
    Proxy对象的所有用法,都是上面的这种形式。不同的只是handle参数的写法。其中new Proxy用来生成Proxy实例,target是表示所要拦截的对象,handle是用来定制拦截行为的对象。
    
    var proxy = new Proxy({}, {
      get: function(target, property) {
        return 35;
      }
    });
    
    proxy.time // 35
    proxy.name // 35
    proxy.title // 35
    上面是一个拦截读取属性的行为的例子。要使Proxy起作用,必须针对Proxy实例进行操作,而不是针对目标对象(target)进行操作。
    

    2、var、let、const之间的区别

    • var声明变量可以重复声明,而let不可以重复声明
    • var是不受限于块级的,而let是受限于块级
    • var会与window相映射(会挂一个属性),而let不与window相映射
    • var可以在声明的上面访问变量,而let有暂存死区,在声明的上面访问变量会报错
    • const声明之后必须赋值,否则会报错
    • const定义不可变的量,改变了就会报错
    • const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报错

    3、使用箭头函数应注意什么?

    • 用了箭头函数,this就不是指向window,而是父级(指向是可变的),在使用=>定义函数的时候,this的指向是定义时所在的对象,而不是使用时所在的对象
    • 不能够使用arguments对象
    • 不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误
    • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数

    this的指向总是让人困扰,但是有了=>以后妈妈再也不用担心你使用this了

    class Animal {
        constructor(){
            this.type = 'animal'
        }
        says(say) {
            setTimeout(function () {
                console.log(this.type + 'says' + say)
            },1000)
        }
    }
    var animal = new Animal()
    animal.says('hi') // undefined says hi
    我们再来看看=>的情况
    
    class Animal() {
        constructor() {
            this.type = 'animal'
        }
        says(say) {
            setTimeout(() => {
                console.log(this.type + ' says ' + say)
            }, 1000)
        }
    }
    var animal = new Animal()
    animal.says('hi') // animal says hi
    

    4、ES6的模板字符串有哪些新特性?并实现一个类模板字符串的功能

    基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定

    在ES5时我们通过反斜杠()来做多行字符串或者字符串一行行拼接。ES6反引号(``)就能解决

      let name = 'web';
            let age = 10;
            let str = '你好,${name} 已经 ${age}岁了'
            str = str.replace(/\$\{([^}]*)\}/g,function(){
                return eval(arguments[1]);
            })
            console.log(str);//你好,web 已经 10岁了    
    

    5、介绍下 Set、Map的区别?

    应用场景
    Set用于数据重组,Map用于数据储存

    Set

    • 成员不能重复
    • 只有键值没有键名,类似数组
    • 可以遍历,方法有add, delete,has

    Map

    • 本质上是健值对的集合,类似集合
    • 可以遍历,可以跟各种数据格式转换

    6、ECMAScript 6 怎么写 class ,为何会出现 class

    ES6的class可以看作是一个语法糖,它的绝大部分功能ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法

    //定义类
    class Point{
        constructor(x,y){
            //构造方法
            this.x = x; //this关键字代表实例对象
            this.y = y;
        }
        toString(){
            return `(${this.x},${this.y})`; //在windows键盘上,``是英文输入法下tab按键上面那个键。
            //'('+this.x+','+this.y+')';
        }
    }
    let p  =new Point(1,2);
    p.toString(); //"(1,2)"
    

    7、Promise构造函数是同步执行还是异步执行,那么 then 方法呢?

    promise构造函数是同步执行的,then方法是异步执行的

    8、setTimeout、Promise、Async/Await 的区别

    事件循环中分为宏任务队列和微任务队列

    其中setTimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行

    promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行

    async函数表示函数里面可能会有异步方法,await后面跟一个表达式

    async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行

    9、promise有几种状态,什么时候会进入catch?

    三个状态:pending、fulfilled、reject

    两个过程:pending -> fulfilled、pending -> rejected

    当pending为rejected时,会进入catch

    10、下面的输出结果是多少

    const promise = new Promise((resolve, reject) => {
        console.log(1);
        resolve();
        console.log(2);
    })
    
    promise.then(() => {
        console.log(3);
    })
    
    console.log(4);
    
    // 1 2 4 3
    

    Promise 新建后立即执行,所以会先输出 1,2,而 Promise.then() 内部的代码在 当次事件循环的结尾立刻执行,所以会继续输出4,最后输出3.

    11、设计一个对象,键名的类型至少包含一个symbol类型,并且实现遍历所有key

    let name = Symbol('name');
     let product = {
        [name]:"洗衣机",    
        "price":799
      };
      Reflect.ownKeys(product); 
      //Reflect.ownKeys() :相当于Object.getOwnPropertyNames(target) concat(Object.getOwnPropertySymbols(target)
    

    12、下面Set结构,打印出的size值是多少

    let s = new Set();
    s.add([1]);
    s.add([1]);
    console.log(s.size);
    //2
    

    两个数组[1]并不是同一个值,它们分别定义的数组,在内存中分别对应着不同的存储地址,因此并不是相同的值

    都能存储到Set结构中,所以size为2

    13、Promise 中reject 和 catch 处理上有什么区别

    • reject 是用来抛出异常,catch 是用来处理异常
    • reject 是 Promise 的方法,而 catch 是 Promise 实例的方法
    • reject后的东西,一定会进入then中的第二个回调,如果then中没有写第二个回调,则进入catch
    • 网络异常(比如断网),会直接进入catch而不会进入then的第二个回调

    14、使用class 手写一个promise

    //创建一个Promise的类
      class Promise{
        constructor(executer){//构造函数constructor里面是个执行器
          this.status = 'pending';//默认的状态 pending
          this.value = undefined//成功的值默认undefined
          this.reason = undefined//失败的值默认undefined
          //状态只有在pending时候才能改变
          let resolveFn = value =>{
            //判断只有等待时才能resolve成功
            if(this.status == pending){
              this.status = 'resolve';
              this.value = value;
            }
          }
          //判断只有等待时才能reject失败
          let rejectFn = reason =>{
            if(this.status == pending){
              this.status = 'reject';
              this.reason = reason;
            }
          }    
          try{
            //把resolve和reject两个函数传给执行器executer
            executer(resolve,reject);
          }catch(e){
            reject(e);//失败的话进catch
          }
        }
        then(onFufilled,onReject){
          //如果状态成功调用onFufilled
          if(this.status = 'resolve'){
            onFufilled(this.value);
          }
          //如果状态失败调用onReject
          if(this.status = 'reject'){
            onReject(this.reason);
          }
        }
      } 
    

    15、如何使用Set去重

    let arr = [12,43,23,43,68,12];
    let item = [...new Set(arr)];
    console.log(item);//[12, 43, 23, 68]
    let item1 = Array.from(new Set(arr));
    console.log(item1);//[12, 43, 23, 68]
    

    16、将下面for循环改成for of形式

    let arr = [11,22,33,44,55];
    let sum = 0;
    for(let i=0;i<arr.length;i++){
        sum += arr[i];
    }
    

    答案:

    let arr = [11,22,33,44,55];
    let sum = 0;
    for(value of arr){
        sum += value;
    }
    

    遍历数组:for in 和 for of

    for(let i in arr ){
        console.log(arr[i])
    }
    for(let i of arr){
        console.log(i);
    }
    

    17、理解 async/await以及对Generator的优势

    • async await 是用来解决异步的,async函数是Generator函数的语法糖
    • 使用关键字async来表示,在函数内部使用 await 来表示异步
    • async函数返回一个Promise对象,可以使用then方法添加回调函数
    • 当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句

    async较Generator的优势:

    • 内置执行器。Generator 函数的执行必须依靠执行器,而 Aysnc 函数自带执行器,调用方式跟普通函数的调用一样
    • 更好的语义。async 和 await 相较于 * 和 yield 更加语义化
    • 更广的适用性。yield命令后面只能是 Thunk 函数或 Promise对象,async函数的await后面可以是Promise也可以是原始类型的值
    • 返回值是 Promise。async 函数返回的是 Promise 对象,比Generator函数返回的Iterator对象方便,可以直接使用 then() 方法进行调用

    18、forEach、for in、for of三者区别

    • forEach更多的用来遍历数组

    • for in 一般常用来遍历对象或json

    • for of数组对象都可以遍历,遍历对象需要通过和Object.keys()

    • for in循环出的是key,for of循环出的是value

      var user = {name:‘张三’,age:10,sex:‘男’}
      for(let index in user){
      console.log(index+’:’+user[index])
      //“name:张三”"age:10"“sex:男”
      }
      for(let index of Object.keys(user)){
      console.log(index+’:’+user[index])
      //“name:张三”"age:10"“sex:男”
      }

    19、说一下es6的导入导出模块

    导入通过import关键字

    // 只导入一个
    import {sum} from "./example.js"
    
    // 导入多个
    import {sum,multiply,time} from "./exportExample.js"
    
    // 导入一整个模块
    import * as example from "./exportExample.js"
    

    导出通过export关键字

    //可以将export放在任何变量,函数或类声明的前面
    export var firstName = 'Michael';
    export var lastName = 'Jackson';
    export var year = 1958;
    
    //也可以使用大括号指定所要输出的一组变量
    var firstName = 'Michael';
    var lastName = 'Jackson';
    var year = 1958;
    export {firstName, lastName, year};
    
    //使用export default时,对应的import语句不需要使用大括号
    let bosh = function crs(){}
    export default bosh;
    import crc from 'crc';
    
    //不使用export default时,对应的import语句需要使用大括号
    let bosh = function crs(){}
    export bosh;
    import {crc} from 'crc';
    

    本文链接:https://blog.csdn.net/qq_39903567/article/details/114990474

    展开全文
  • JS、ES6es6的新特性 const let 模板字符串 箭头函数 函数的参数默认值 对象和数组解构 for...of 和 for...in 怎么用es5实现promise Promise是ES6才引入的一个东西,他是一种异步编程的解决方案 if有...

    JS、ES6篇

    es6的新特性

    • const let
    • 模板字符串
    • 箭头函数
    • 函数的参数默认值
    • 对象和数组解构
    • for...of 和 for...in(for in更适合遍历对象,for of更适合遍历数组)

    怎么用es5实现promise

    Promise是ES6才引入的一个东西,他是一种异步编程的解决方案

    if有作用域吗

    只有函数有作用域,if是没有作用域的。

    但是有一种情况会让if看上去有作用域,就是在if {}语句中,使用const、let,他们会有块级作用域。(因为const、let才拥有块级作用域 )

    原型链和作用域链的区别

    (1)原型链
    当访问一个对象的某个属性时,会先在这个对象本身的属性上找,如果没有找到,会去这个属性的__proto__属性上找,即这个构造函数的prototype,如果还没找到,就会继续在__proto__上查找,
    直到最顶层,找不到即为undefined。这样一层一层往上找,彷佛是一条链子串起来,所以叫做原型链。

    (2)作用域链
    变量取值会到创建这个变量的函数的作用域中取值,如果找不到,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。

    (3)区别
    作用域是对变量而言,原型链是对于对象的属性而言
    作用域链的顶层是window,原型链的顶层是Object

    js判断类型

    1、typeof
    检测不出null 和 数组,结果都为object,所以typeof常用于检测基本类型

    2、instanceof
    不能检测出number、boolean、string、undefined、null、symbol类型,所以instancof常用于检测复杂类型以及级成关系

    3、constructor
    null、undefined没有construstor方法,因此constructor不能判断undefined和null。
    但是contructor的指向是可以被改变,所以不安全

    4、Object.prototype.toString.call
    全类型都可以判断

    数据类型怎么检测

    1、typeof
    例:console.log(typeof true) // boolean

    2、instanceof
    例:console.log([1,2] instanceof Array) // true

    3、constructor
    例: console.log([1, 2].constructor === Array) // ture

    4、Object.prototype.toString.call
    例:Object.prototype.toString.call([1, 2]) // [object Array]

    普通函数和箭头函数的区别

    1、普通函数
    可以通过bind、call、apply改变this指向
    可以使用new

    2、箭头函数
    本身没有this指向,
    它的this在定义的时候继承自外层第一个普通函数的this
    被继承的普通函数的this指向改变,箭头函数的this指向会跟着改变
    箭头函数外层没有普通函数时,this指向window
    不能通过bind、call、apply改变this指向
    使用new调用箭头函数会报错,因为箭头函数没有constructor

    如何用原生js给一个按钮绑定两个onclick事件?

    var btn = document.getElementById('btn')
    btn.addEventListener('click', fn1)
    btn.addEventListener('click', fn2)
    
    function fn1 () {
      console.log('我是方法1')  
    }
    
    function fn2 () {
      console.log('我是方法2')  
    }

    document.write和innerHTML的区别

    document.write 将内容写入页面,清空替换掉原来的内容,会导致重绘

    document.innerHTML 将内容写入某个Dom节点不会重绘

    栈和堆的区别

    1、堆
    动态分配内存,内存大小不一,也不会自动释放

    2、栈
    自动分配相对固定大小的内存空间,并由系统自动释放

    3、基本类型都是存储在栈中,每种类型的数据占用的空间的大小是确定的,并由系统自动分配和释放。内存可以及时回收。

    4、引用类型的数据都是存储在堆中。准确说是栈中会存储这些数据的地址指针,并指向堆中的具体数据。

    总的来说:里面存放的是基本数据类型(num string boolen null undefined),栈里面放的是引用数据类型(数组和对象)

     打个比方

    [注] 栈内存入的是arr 是一个地址 堆内存入的是基本数据类型 

    undefined 和 null 区别

    1、null
    什么都没有,表示一个空对象引用(主动释放一个变量引用的兑现那个,表示一个变量不再指向任何引用地址)
    2、undefined
    没有设置值的变量,会自动赋值undefined
    3、区别
    typeof undefined             // undefined
    typeof null                  // object
    null === undefined           // false
    null == undefined            // true

    eval()的作用

    eval(string) 函数计算 JavaScript 字符串,并把它作为脚本代码来执行

    如果参数是一个表达式,eval() 函数将执行表达式;

    如果参数是Javascript语句,eval()将执行 Javascript 语句;

    如果执行结果是一个值就返回,不是就返回undefined,如果参数不是一个字符串,则直接返回该参数。

    特殊:eval("{b:2}") // 声明一个对象

            eval("({b:2})") // 返回对象{b:2}

    JS哪些操作会造成内存泄露

    内存泄漏是指一块被分配的内存既不能使用,也不能回收,直到浏览器进程结束。
    1、意外的全局变量
    2、闭包
    3、没有清理的dom元素
    dom元素赋值给变量,又通过removeChild移除dom元素。但是dom元素的引用还在内存中
    4、被遗忘的定时器或者回调

    谈谈垃圾回收机制方式及内存管理

    JavaScript 在定义变量时就完成了内存分配。当不在使用变量了就会被回收,因为其开销比较大,垃圾收集器会定期(周期性)找出那些不再继续使用的变量,然后释放其内存。
    (1)垃圾回收
    标记清除法
    当变量进入环境时,将这个变量标记为'进入环境'。当标记离开环境时,标记为‘离开环境’。离开环境的变量会被回收
    引用技计数法
    跟踪记录每个值被引用的次数,如果没有被引用,就会回收
    (2)内存管理
    内存分配=》内存使用=》内存回收

    什么是闭包,如何使用它,为什么要使用它?

    (1)闭包就是能够读取其它函数内部变量的函数
    (2)使用方法:在一个函数内部创建另一个函数
    (3)最大用处有两个:读取其他函数的变量值,让这些变量始终保存在内存中
    (4)缺点:会引起内存泄漏(引用无法被销毁,一直存在)

    请解释JSONP的工作原理,以及它为什么不是真正的AJAX

    JSONP 是一种非正式传输协议,允许用户传递一个callback给服务端,然后服务端返回数据时会将这个callback 参数作为函数名来包裹住 JSON 数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
    当GET请求从后台页面返回时,可以返回一段JavaScript代码,这段代码会自动执行,可以用来负责调用后台页面中的一个callback函数。
    它们的实质不同
    ajax的核心是通过xmlHttpRequest获取非本页内容
    jsonp的核心是动态添加script标签调用服务器提供的js脚本
    jsonp只支持get请求,ajax支持get和post请求

    请解释一下JavaScript的同源策略

    同源指协议,域名,端口相同,
    同源策略是一种安全协议,指一段脚本只能读取来自同一来源的窗口和文档的属性。

    关于JS事件冒泡与JS事件代理(事件委托)

    (1)事件冒泡
    当一个元素接收到事件的时候 会把他接收到的事件传给自己的父级,一直到window,过程就像冒泡泡 。如果在某一层想要中止冒泡,使用 event.stopPropagation() 。
    但是当大量标签大量事件的时候先然不可能为每个元素都加上事件,(事件绑定占用事件,浏览器要跟踪每个事件,占用更多内存。而且并不是所有事件都会被用户使用到)。所以需要事件委托来解决这个问题。
    (2)事件委托
    将事件给最外层的元素,自己不实现逻辑,由最外层元素来代理。(判断事件源,做不同处理)

    new操作符到底做了什么

    (1)var obj = new Fun()// 做了三件事

    var obj  = {}  // 创建一个空对象
    obj.__proto__ = Fun.prototype //空对象的__proto__指向构造函数的原型对象
    Fun.call(obj) // 构造函数this指向替换成obj

    (2)实现一个new
    function _new(fn, ...arg) {
        const obj = Object.create(fn.prototype);
        const ret = fn.apply(obj, arg);
        // 根据规范,返回 null 和 undefined 不处理,依然返回obj,不能使用
        // typeof result === 'object' ? result : obj
        return ret instanceof Object ? ret : obj;
    }

    js延迟加载的方式有哪些?

    (1)defer属性
            <script src="test.js" defer="defer"></script>
            立即下载,但是会等到整个页面都解析完成之后再执行
    (2)async属性
            <script src="test.js" async></script>
            不让页面等待脚本下载和执行(异步下载),但是无法控制加载的顺序
    (3)动态创建script标签
    (4)使用定时器延迟
    (5)让js最后加载

    promise、async有什么区别

    1、什么是Async/Await
    async/await是写异步代码的新方式,使用的方式看起来像同步
    async/await是基于Promise实现的,它不能用于普通的回调函数。

    2、什么是promise
    为了解决异步嵌套而产生,让代码更易于理解

    区别,async/await让代码更像同步,进一步优化了代码

    == 和 ===的区别,什么情况下用相等==

    ”==” 是判断转换后的值是否相等, 

    ”===” 是判断值及类型是否完全相等

    不需要判断类型时可以使用==

    bind、call、apply的区别

    let o1 = {
        a: 1
    };
    let o2 = {
        a: 2
    };
    let o3 = {
        a: 3
    };

    function fn(b, c) {
        console.log(this.a + b + c);
    };

    1、bind
    var c = b.bind(o1, 2, 3); // 返回一个已经切换this指向的新函数
    c(1,1)//依旧是6 因为bind指定this后不会再改变

    2、apply
    b.apply(a, [1, 1])  // 将b添加到a环境中
    第一个参数是this指向的新环境
    第二个参数是要传递给新环境的参数
    注意: 第一个参数为null时表示指向window

    3、call
    b.call(a, 1, 1) // 将b添加到a环境中
    第一个参数是this指向的新环境
    第二、三...个参数是传递给新环境的参数
    注意: 第一个参数为null时表示指向window

    小结: bind方法可以让函数想什么时候调用就什么时候调用。apply、call方法只是临时改变了this指向。

    call、apply区别

    相同点:都是重定向this指针的方法。
    不同点:call和apply的第二个参数不相同,call是若干个参数的列表。apply是一个数组

    let mock = { value : 1 };
    function mockNum(){
     console.log('value',this.value)
    }
    mockNum.call(mock) // 改变了函数中this的指向,当前this指向了mock对象
    
    //转换一下实现方法就是
    
    let mock = {
      value:1;
      mockNum:function(){
         console.log('value',this.value)
      }
    }
    mock.mockNum();
    //所以经过上面这个操作的演化而来的结果就是如下步骤:
    //1. 将函数设为一个对象的属性
    //2. 并将这个函数的属性调用
    //3. 删除该函数
    
    Function.prototype.Mycall = function(context){
      let obj = context || window;
      obj.fn = this; //   这一步可以看做是this其实就指的当前函数。
      let args = [...arguments].slice(1); // 返回删除第一个元素的数组;
      let result = obj.fn(...args); // 调用函数
      delete obj.fn;
      return result;
    }
    
    // 操作一下
    let mock = { value : 1 };
    function mockNum(){
      console.log('value',this.value);
    }
    mockNum.Mycall(mock) // value 1
    
    
    
    //apply的写法
    Function.prototype.Myapply = function (context){
        let obj = context || window;
        obj.fn = this;
        let result = arguments[1] ? obj.fn(arguments[1]) : obj.fn([]);
        delete obj.fn;
        return result;
    }
    let mock3 = {
      arr: [1, 2, 3, 4, 5],
    };
    function arrx2(arr) {
      return this.arr.concat(arr).map((x) => x * 2);
    }
    console.log("arrx2", arrx2.myApply(mock3));

    介绍暂时性死区

    在代码块内,使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。

    两个对象如何比较

    有思路即可,步骤:
    (1)判断两个类型是否对象
    (2)判断两个对象key的长度是否一致
    (3)判断属性value值的数据类型,根据不同数据类型做比较
        a、是对象,重复这个步骤
        b、是数组,转字符串比较
        c、是基本类型,直接判断

    介绍各种异步方案

    (1)回调函数
    (2)promise
    (3)async/await

    Promise和Async处理失败的时候有什么区别

    (1)Promise错误可以在构造体里面被捕获,而async/await返回的是promise,可以通过  catch直接捕获错误。
    (2)await 后接的Promise.reject都必须被捕获,否则会中断执行

    JS为什么要区分微任务和宏任务

    (1)js是单线程的,但是分同步异步
    (2)微任务和宏任务皆为异步任务,它们都属于一个队列
    (3)宏任务一般是:script,setTimeout,setInterval、setImmediate
    (4)微任务:原生Promise
    (5)遇到微任务,先执行微任务,执行完后如果没有微任务,就执行下一个宏任务,如果有微任务,就按顺序一个一个执行微任务

    Promise和setTimeout执行先后的区别

    Promise是微任务,setTimeout是宏任务,先执行微任务,如有还有微任务执行完微任务再执行下一个宏任务

    添加原生事件不移除为什么会内存泄露

    setTimeout(0)和setTimeout(1)之间的区别

    定时器表面意思是过指定时间后执行,但是真正的含义是每过指定时间后,会有fn进入事件队列
    (1)setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行
    (2)setTimeout(fn,1) 1毫秒后执行,但是因为要先执行前面的宏任务,所以不一定就是在一毫秒后执行,取决于队列里任务执行的速度

    for..in 和 object.keys的区别

    Object.keys不会遍历继承的原型属性
    for...in 会遍历继承的原型属性

    XML和JSON的区别?

    1.xml是重量级的,json是轻量级的。
    2.xml在传输过程中比较占带宽,json占带宽少,易于压缩。
    3.xml和json都用在项目交互下,xml多用于做配置文件,json用于数据交互。

    说说你对AMD和Commonjs的理解

    两者都是为了实现模块化编程而出现的
    (1)commonjs
    通常被应用于服务器,在服务器端,模块的加载和执行都在本地完成,因此,CommonJS并不要求模块加载的异步化。
    核心思想:xsdz独文件就是一个模块,通过require方法来同步加载要依赖的模块,然后通过extports或则module.exports来导出需要暴露的接口。
    (2)AMD
    可以实现异步加载依赖模块,预加载,在并行加载js文件同时,还会解析执行该模块。虽然可以并行加载,异步处理,但是加载顺序不一定
    (3)CMD
    懒加载,虽然会一开始就并行加载js文件,但是不会执行,而是在需要的时候才执行。

    js的涉及模式

    单例模式、策略模式、代理模式、迭代器模式...等等

    ["1", "2", "3"].map(parseInt) 答案是多少?

    (1)map用法:
    arr.map(function(el, index, arr) {
        return el
    })
    map方法接收一个函数参数,并且这个函数可以接收三个参数
    el:遍历过程中的当前项,
    index:遍历过程中的当前下标
    arr: 原数组

    (2)parseInt用法:
    parseInt(str, num)
    根据num解析str,并返回一个整数。
    str: 要解析的字符串,如果字符第一个数不能被转换,返回NaN。
    num: 基数,介于 2 ~ 36 之间,如果传0,则默认用10计算。num不在区间内,返回NaN

    (3)所以这道题,关键点就在num
    el和index 相当于 str 和 num 带入一下
    parseInt('1', 0) // '1' 用基数10算 为1
    parseInt('2', 1) // NaN 1不在2-36内
    parseInt('3', 2) // NaN  2进制内没有超过1的存在

    常见兼容性问题?

    (1)事件对象兼容
            e  = e || window.event;
    (2)阻止事件冒泡兼容
            event.stopPropagation? event.stopPropagation():event.cancelBubble=true;
    (3)阻止默认行为兼容
            evt.preventDefault?evt.preventDefault():evt.returnValue=false;
    (4)事件监听兼容
            addEventListener  ? addEventListener  : attachEvent()
            removeEventListener() ? removeEventListener() : detachEvent()
    (5)事件目标对象兼容
            var src = event.target || event.srcElement;

    说说你对promise的了解

    (1)promise是为解决异步处理回调金字塔问题而产生的
    (2)有三种状态,pengding、resolve、reject,状态一旦决定就不会改变
    (3)then接收resolve(),catch接收reject()

    介绍js有哪些内置对象?

    String对象、Array对象、Object对象、Number对象
    Math对象、Date对象、Boolean对象

    说几条写JavaScript的基本规范?

    1、不要在同一行声明多个变量
    2、使用 === 和 !== 来比较
    3、不要使用全局函数
    4、变量在使用之前的位置声明(减少变量提升干扰)
    5、if用花括号包起来即使只有一行
    6、写注释

    JavaScript有几种类型的值?,你能画一下他们的内存图吗?

    栈:原始数据类型(Undefined  Null  Boolean  Number  String)
    堆:引用数据类型(对象、数组、函数)

    href="#"与href="javascript:void(0)的区别"?

    1、# 包含了一个位置信息,默认的锚是#top 也就是网页的上端。在页面很长的时候会使用 # 来定位页面的具体位置,格式为:# + id。

    2、javascript:void(0), 仅仅表示一个死链接。

    如果你要定义一个死链接请使用 javascript:void(0) 

    对this的理解

    1、单独使用,this表示全局对象
    2、在函数中,this表示全局对象
    3、在对象的一个函数方法中,this表示这个对象
    4、可以通过apply、bind来更改this的指向

    ps: this永远指向的是最后调用它的对象,仅当它在对象的一个函数方法中时会有差异

    promise.all 返回什么

    如果没有报错,返回执行结果[res1, res2,...]
    如果报错,则返回第一个报错的promise的结果

    多个await的执行顺序,如果要同步执行如何实现?

    使用Promise.all
    promise.all是等所有异步操作都完成之后返回结果,相当于让这些异步同步了

     拖拽会用到哪些事件

    • dragstart:拖拽开始时在被拖拽元素上触发此事件,监听器需要设置拖拽所需数据,从操作系统拖拽文件到浏览器时不触发此事件.
    • dragenter:拖拽鼠标进入元素时在该元素上触发,用于给拖放元素设置视觉反馈,如高亮
    • dragover:拖拽时鼠标在目标元素上移动时触发.监听器通过阻止浏览器默认行为设置元素为可拖放元素.
    • dragleave:拖拽时鼠标移出目标元素时在目标元素上触发.此时监听器可以取消掉前面设置的视觉效果.
    • drag:拖拽期间在被拖拽元素上连续触发
    • drop:鼠标在拖放目标上释放时,在拖放目标上触发.此时监听器需要收集数据并且执行所需操作.如果是从操作系统拖放文件到浏览器,需要取消浏览器默认行为.
    • dragend:鼠标在拖放目标上释放时,在拖拽元素上触发.将元素从浏览器拖放到操作系统时不会触发此事件. 

    请列举jquery中的选择器

    #id,.class,element,:first,:even,:eq(index),:contains(text)

    注:这些选择器都是基于$()的

    Javascript中的定时器有哪些?他们的区别及用法是什么?

    setTimeout 只执行一次
    setInterval 会一直重复执行

    请描述一下 cookies sessionStorage和localstorage区别

    编写一个方法去掉数组里面重复的内容

    var arr=['abc','abcd','sss','2','d','t','2','ss','f','22','d']
    //定义一个新的数组
    var s=[]
    //遍历数组
    for(var i=0;i<arr.length;i++){
            if(s.indexOf(arr[i]) == -1){
                //判断在s数组中是否存在,不存在则push到s数组 
                //-1是不存在 1是存在
                s.push(arr[i])中
            }
    }
    console.log(s)
    //输出结果:["abc","abcd","sss","2","d","t","ss","f","22"]

    什么是ajax? ajax的步骤?

    ajax(异步javascript xml) 能够刷新局部网页数据而不是重新加载整个网页。
    如何使用ajax?
    第一步,创建xmlhttprequest对象

    var xmlhttp =new XMLHttpRequest();
    //XMLHttpRequest对象用来和服务器交换数据。
    var xhttp;
    if (window.XMLHttpRequest) {
    //现代主流浏览器
    xhttp = new XMLHttpRequest();
    } else {
    // 针对浏览器,比如IE5或IE6
    xhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }

    第二步,使用xmlhttprequest对象的open()和send()方法发送资源请求给服务器。
    第三步,使用xmlhttprequest对象的responseText或responseXML属性获得服务器的响应。
    第四步,onreadystatechange函数,当发送请求到服务器,我们想要服务器响应执行一些功能就需要使用onreadystatechange函数,每次xmlhttprequest对象的readyState发生改变都会触发onreadystatechange函数。

    http和https有何区别?

    http是HTTP协议运行在TCP之上。所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。
    https是HTTP运行在SSL/TLS之上,SSL/TLS运行在TCP之上。所有传输的内容都经过加密,
    加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。
    此外客户端可以验证服务器端的身份,如果配置了客户端验证,服务器方也可以验证客户端的身份。

    常见的HTTP状态码 

    2开头 (请求成功)表示成功处理了请求的状态代码。
    200   (成功)  服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
    201   (已创建)  请求成功并且服务器创建了新的资源。
    202   (已接受)  服务器已接受请求,但尚未处理。
    203   (非授权信息)  服务器已成功处理了请求,但返回的信息可能来自另一来源。
    204   (无内容)  服务器成功处理了请求,但没有返回任何内容。
    205   (重置内容) 服务器成功处理了请求,但没有返回任何内容。
    206   (部分内容)  服务器成功处理了部分 GET 请求。


    3开头 (请求被重定向)表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。
    300   (多种选择)  针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。
    301   (永久移动)  请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
    302   (临时移动)  服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
    303   (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
    304   (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
    305   (使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。
    307   (临时重定向)  服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。


    4开头 (请求错误)这些状态代码表示请求可能出错,妨碍了服务器的处理。
    400   (错误请求) 服务器不理解请求的语法。
    401   (未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
    403   (禁止) 服务器拒绝请求。
    404   (未找到) 服务器找不到请求的网页。
    405   (方法禁用) 禁用请求中指定的方法。
    406   (不接受) 无法使用请求的内容特性响应请求的网页。
    407   (需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。
    408   (请求超时)  服务器等候请求时发生超时。
    409   (冲突)  服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。
    410   (已删除)  如果请求的资源已永久删除,服务器就会返回此响应。
    411   (需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。
    412   (未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。
    413   (请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。
    414   (请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。
    415   (不支持的媒体类型) 请求的格式不受请求页面的支持。
    416   (请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。
    417   (未满足期望值) 服务器未满足"期望"请求标头字段的要求。


    5开头(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。
    500   (服务器内部错误)  服务器遇到错误,无法完成请求。
    501   (尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。
    502   (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。
    503   (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
    504   (网关超时)  服务器作为网关或代理,但是没有及时从上游服务器收到请求。
    505   (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。

    如何进行网站性能优化

    (1)目的: 从用户角度而言,优化能够让页面加载得更快、对用户的操作响应得更及时,能够给用户提供更为友好的体验。从服务商角度而言,优化能够减少页面请求数、或者减小请求所占带宽,能够节省可观的资源。

    (2)措施:

    ——减少HTTP请求数。
    ——使用缓存。
    ——脚本的无阻塞加载。
    ——内联脚本的位置优化等。
    ——Javascript中的DOM 操作优化、CSS选择符优化。
    ——图片编码优化,懒加载。
    ——使用负载均衡方案。

    什么是mvvm,mvc?区别?

    MVC(Model-View-Controller)

    MVC是比较直观的架构模式,用户操作
     ->View(负责接收用户的输入操作)
     ->Controller(业务逻辑处理)->Model(数据持久化)->View(将结果反馈给View)。             MVC使用非常广泛,比如JavaEE中的SSH框架。

    MVVM(Model-View-ViewModel)

    ->Model:数据层

    ->View:视图层

    ->ViewModel:数据视觉层

    px和em的区别

    px表示像素 (计算机屏幕上的一个点:1px = 1/96in),是绝对单位,不会因为其他元素的尺寸变化而变化;
    em表示相对于父元素的字体大小。em是相对单位 ,没有一个固定的度量值,而是由其他元素尺寸来决定的相对值。

    优雅降级和渐进增强

    渐进增强(Progressive Enhancement):一开始就针对低版本浏览器进行构建页面,完成基本的功能,然后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。
    优雅降级(Graceful Degradation):一开始就构建站点的完整功能,然后针对浏览器测试和修复。比如一开始使用 CSS3 的特性构建了一个应用,然后逐步针对各大浏览器进行 hack 使其可以在低版本浏览器上正常浏览。

    其实渐进增强和优雅降级并非什么新概念,只是旧的概念换了一个新的说法。在传统软件开发中,经常会提到向上兼容和向下兼容的概念。渐进增强相当于向上兼容,而优雅降级相当于向下兼容。

    怎样添加、移除、移动、复制、创建和查找节点?

    (1)创建新节点

    createDocumentFragment() //创建一个DOM片段

    createElement() //创建一个具体的元素

    createTextNode() //创建一个文本节点

    (2)添加、移除、替换、插入

    appendChild() //添加

    removeChild() //移除

    replaceChild() //替换

    insertBefore() //插入

    (3)查找

    getElementsByTagName() //通过标签名称

    getElementsByName() //通过元素的Name属性的值

    getElementById() //通过元素Id,唯一性

    浏览器是如何渲染页面的?

    1.解析HTML文件,创建DOM树。
      自上而下,遇到任何样式(link、style)与脚本(script)都会阻塞(外部样式不阻塞后续外部脚本的加载)。
    2.解析CSS。优先级:浏览器默认设置<用户设置<外部样式<内联样式

    从输入url到显示页面,都经历了什么?

    1、首先,在浏览器地址栏中输入url。
    2、浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则跳到第三步操作。
    3、在发送http请求前,需要域名解析(DNS解析)(DNS(域名系统,Domain Name System)是互联网的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网,而不用去记住IP地址。),解析获取相应的IP地址。
    4、浏览器向服务器发起tcp连接,与浏览器建立tcp三次握手。(TCP即传输控制协议。TCP连接是互联网连接协议集的一种。)
    5、握手成功后,浏览器向服务器发送http请求,请求数据包。
    6、服务器处理收到的请求,将数据返回至浏览器。
    7、浏览器收到HTTP响应。
    8、读取页面内容,浏览器渲染,解析html源码。
    9、生成Dom树、解析css样式、js交互。
    10、客户端和服务器交互。
    11、ajax查询。

    JavaScript中如何检测一个变量是一个String类型?请写出函数实现

    typeof(obj) === "string"
    typeof obj === "string"
    obj.constructor === String

    判断一个字符串中出现次数最多的字符,统计这个次数

        function aa() {
            let str = 'asdfssaaasasasasaa';
            let json = {};
            for (let i = 0; i < str.length; i++) {
                let index = str[i]
                if (!json[index]) {
                    json[index] = 1
                } else {
                    json[index]++
                }
            }
            let iMax = 0
            let iIndex = ''
            for(let k in json){
                if(json[k]>iMax){
                    iMax =json[k]
                    iIndex=k
                }
            }
            console.log('出现次数最多的是:' + iIndex + '出现' + iMax + '次');

    $(document).ready()方法和window.onload有什么区别?

    (1)、window.onload方法是在网页中所有的元素(包括元素的所有关联文件)完全加载到浏览器后才执行的。
    (2)、$(document).ready() 方法可以在DOM载入就绪时就对其进行操纵,并调用执行绑定的函数。

    window.onLoad和DOMContentLoaded事件的先后顺序?

    顺序

    一般情况下,DOMContentLoaded事件要在window.onload之前执行,当DOM树构建完成的时候就会执行 DOMContentLoaded事件,而window.onload是在页面载入完成的时候,才执行

    区别

    1、当 onload事件触发时,页面上所有的DOM,样式表,脚本,图片,flash都已经加载完成了。

    2、当DOMContentLoaded事件触发时,仅当DOM加载完成,不包括样式表,图片,flash。

    JavaScript中的变量声明提升?

    函数声明和变量声明总是被JavaScript解释器隐式地提升到包含他们的作用域的最顶端。

    function优先声明于var。

    函数表达式中只会提升名称,函数体只有在执行到赋值语句时才会被赋值。

    function foo() {
         bar(); 
         var x = 1; 
    } 
    function foo() {//等同于
       var x;
       bar(); 
       x = 1; 
    } 
    function test() { 
        foo(); // TypeError "foo is not a function" 
        bar(); // "this will run!"
        var foo = function () {}// 函数表达式被赋值给变量'foo' 
        function bar() {}// 名为'bar'的函数声明 
    }

    请写出console.log中的内容

        var msg = 'hello';//顶级作用域window下有个变量msg
        function great(name, attr) {
            var name = 'david';
            var greating = msg + name + '!';
            var msg = '你好';
            for (var i = 0; i < 10; i++) {
                var next = msg + '你的id是' + i * 2 + i;
            }
            console.log(arguments[0]);//david
            console.log(arguments[1]);//undefind
            console.log(greating);//undefineddavid!
            console.log(next);//你好你的id是189
        }
    
        great('Tom')

    注:在作用域内会先调用作用域内的变量,如果var变量在声明之前就使用了就是变量提升是undefined

    JS延迟加载的方式有哪些?

    1、 JS的延迟加载有助与提高页面的加载速度。

    2、 defer 和async 、动态创建DOM 方式(用得最多)、按需异步载入

    3、 JSdefer:延迟脚本。立即下载,但延迟执行(延迟到整个页面都解析完毕后再运行),按照脚本出现的先后顺序执行。

    4、 async :异步脚本。下载完立即执行,但不保证按照脚本出现的先后顺序执行。

    为什么避免v-if和v-for一起使用

    当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级;
    可以先对数据在计算数据中进行过滤,然后再进行遍历渲染;
    操作和实现起来都没有什么问题,页面也会正常展示。但是会带来不必要的性能消耗;

    深拷贝和浅拷贝的方法

    浅拷贝  Object.assign()    //es6的方法

    var obj={aa:1,b:{item:'45'}};
    var newObj=Object.assign({},obj);
    obj.aa=2;
    obj.b.item='kk';
    console.log(newObj.aa);        //1
    console.log(newObj.b.item);    //kk

    深拷贝  JSON.parse(JSON.stringify(obj))

      function cloneDeep(source) {
          if (!isObject(source)) return source; // 非对象返回自身
          var target = Array.isArray(source) ? [] : {};
          for (var key in source) {
            if (source.hasOwnProperty(i)) {
              if (isObject(source[key])) {
                target[key] = cloneDeep(source[key]); // 注意这里
              } else {
                target[key] = source[key];
              }
            }
          }
          return target;
        }
        function isObject(obj) {
          return typeof obj === 'object' && obj != null;
        }

    如何统计网⻚上出现了多少种标签 

    获取所有的DOM节点

    document.querySelectorAll('*')

    NodeList集合转化为数组

    [...document.querySelectorAll('*')]

    获取数组每个元素的标签名 

    [...document.querySelectorAll('*')}.map(ele => ele.tagName)
    

    去重

    new Set([...document.querySelectorAll('*').map(ele=>ele.tagName)).size
    展开全文
  • es6 class、module、generator/ promise/ async/ await、let/ const、解构赋值、块级作用域、函数默认参数、箭头函数、set数据结构、es6基础 class module generator/ promise/ async/ await let/ const ...
  • ECMAScript 6.0(简称ES6)是 JavaScript 语言的下一代标准。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。 let const 在es6之前,定义变量都是使用var,但是var存在...
  • 2020年最新前端面试题(包含ES6+)

    千次阅读 多人点赞 2020-08-23 12:58:39
    这是博主自己整理的一些前端所需要掌握的知识点,若有错,请评论指出。觉得博主需要补充的也可以留言哦 文章目录1.引用类型和基本类型2.JavaScript中的堆和栈3.var与let、const的区别4.JavaScript 严格模式(use ...
  • 2020年Web前端面试题及答案----ES6

    千次阅读 2020-12-14 16:36:28
    ES6添加了一系列新的语言特性,其中一些特性比其它更具有开创性以及更广的可用性。 列举常用的ES6方法: let、const 箭头函数 类的支持 字符串模块 Promises 箭头函数需要注意哪些地方? 当要求动态上下文的时候,...
  • JavaScript、ES6基础面试题及答案解析② 1、js基本数据类型有哪些? 答:字符串String,数值Number,布尔 boolean,null,undefined,对象,数组 2、Ajax如何使用 一个完整的AJAX请求包括五个步骤: 创建...
  • Web前端面试题及答案——ES6

    千次阅读 2019-02-26 23:20:13
    1、let、const和var的区别 let用于声明变量,const...let和const不存在变量提升、重复声明变量、使用未经声明的变量(ES6强制开启严格模式)都会报错。var有变量提升、可以重复声明变量。 console.log(i); // ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,357
精华内容 4,542
关键字:

前端es6面试题