1.介绍 js 的基本数据类型
2.介绍 js 有哪些内置对象?
3.null 和 undefined 的区别?
4.JavaScript 原型,原型链? 有什么特点?
5.js 获取原型的方法?
6.javascript 创建对象的几种方式?
- 第一种是工厂模式。
- 第二种是构造函数模式。
- 第三种模式是原型模式
- 第四种模式是组合使用构造函数模式和原型模式,
- 第五种模式是动态原型模式
- 第六种模式是寄生构造函数模式
7.JavaScript 继承的几种实现方式?
8.谈谈 This 对象的理解
name = "Bale";
function sayName (){
console.log(this.name);
};
sayName(); //"Bale"
其次,如果函数被调⽤的位置存在上下⽂对象时,那么函数是被隐式绑定的。
function f() {
console.log( this.name );}
var obj = {name: "Messi",f: f};
obj.f(); //被调⽤的位置恰好被对象obj拥有,因此结果是Messi
function f() {
console.log( this.name );
}
var obj = {name: "Messi",};
var obj1 = {name: "Bale"};
f.bind(obj)();
//Messi ,由于bind将obj绑定到f函数上后返回⼀个新函数,因此需要再在后⾯加上括号进⾏执⾏,
这是bind与apply和call的区别function Person(name) {
this.name = name;
console.log(name);}
var person1 = new Person('Messi');
//Messi9.eval 是做什么的?
10.["1", "2", "3"].map(parseInt) 答案是多少?
parseInt(string,radix) 解析一个字符串,并返回一个整数。
第一次:parseInt("1",0)
//第二参数为0相当于默认的方法,所以得到了1第二次:parseInt("2",1)
//由于第二参数为1小于2了,所以得到了NaN第三次:parseInt("3",2)
//虽然第二参数2符合条件,但是参数一3并不能当成二进制解释,所以得到了NaN11.什么是闭包,为什么要用它?(重点)
(function() {
var a = 1;
function add() {
var b = 2
var sum = b + a
console.log(sum); // 3
}
add()
})()function Person(){
var name = 'cxk';
this.getName = function(){
return name;
}
this.setName = function(value){
name = value;
}}
const cxk = new Person()
console.log(cxk.getName()) //cxk
cxk.setName('jntm')
console.log(cxk.getName()) //jntm
console.log(name) //name is not defined
//函数体内的 var name = 'cxk' 只有 getName 和 setName 两个函数可以访问,
外部⽆法访问,相对于将变量私有化。12.对于 JSON 的了解?
13.js 延迟加载的方式有哪些?
14.同步和异步的区别?
15.什么是浏览器的同源政策?
16.如何解决跨域问题?
17.js 的几种模块规范?
- 第一种是 CommonJS 方案
- 第二种是 AMD 方案
- 第三种是 CMD 方案
- 第四种方案是 ES6 提出的方案,使用 import 和 export 的形式来导入导出模块。
18.AMD 和 CMD 规范的区别?
它们之间的主要区别有两个方面。
(1)第一个方面是在模块定义时对依赖的处理不同。AMD 推崇依赖前置,在定义模块的时候就要声明其依赖的模块。而 CMD 推崇 就近依赖,只有在用到某个模块的时候再去 require。
(2)第二个方面是对依赖模块的执行时机处理不同。首先 AMD 和 CMD 对于模块的加载方式都是异步加载,不过它们的区别在于 模块的执行时机,AMD 在依赖模块加载完成后就直接执行依赖模块,依赖模块的执行顺序和我们书写的顺序不一定一致。而 CMD 在依赖模块加载完成后并不执行,只是下载而已,等到所有的依赖模块都加载好后,进入回调函数逻辑,遇到 require 语句 的时候才执行对应的模块,这样模块的执行顺序就和我们书写的顺序保持一致了。
// CMD
define(function(require, exports, module) {
var a = require("./a");
a.doSomething();
// 此处略去 100 行
var b = require("./b"); // 依赖可以就近书写
b.doSomething();
// ...
});
// AMD 默认推荐
define(["./a", "./b"], function(a, b) {
// 依赖必须一开始就写好
a.doSomething();
// 此处略去 100 行
b.doSomething();
// ...
});19.简单介绍一下 JS的垃圾回收机制?
工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存
工作流程:
1. 垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。
2. 去掉环境中的变量以及被环境中的变量引用的变量的标记。
3. 再被加上标记的会被视为准备删除的变量。
4. 垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。
20.哪些操作会造成内存泄漏?
原因:全局变量,不会被回收。
解决:使用严格模式避免。
2. 闭包引起的内存泄漏
原因:闭包可以维持函数内局部变量,使其得不到释放。
解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用。
3. 没有清理的DOM元素引用
原因:虽然别的地方删除了,但是对象中还存在对dom的引用
解决:手动删除。
4. 被遗忘的定时器或者回调
原因:定时器中有dom的引用,即使dom删除了,但是定时器还在,所以内存中还是有这个dom。
解决:手动删除定时器和dom。
5. 子元素存在引用引起的内存泄漏
原因:div中的ul li 得到这个div,会间接引用某个得到的li,那么此时因为div间接引用li,即使li被清空,也还是在内存中,并且只要li不被删除,他的父元素都不会被删除。
解决:手动删除清空。
21.js 中的深浅拷贝实现?
// 浅拷贝的实现;
function shallowCopy(object) {
// 只拷贝对象
if (!object || typeof object !== "object") return;
// 根据 object 的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object) ? [] : {};
// 遍历 object,并且判断是 object 的属性才拷贝
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = object[key];
}
}
return newObject;
}
// 深拷贝的实现;
function deepCopy(object) {
if (!object || typeof object !== "object") return;
let newObject = Array.isArray(object) ? [] : {};
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] =
typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
}
}
return newObject;
}22.call、apply、bind区别
call 接收多个参数,第一个为函数上下文也就是this,后边参数为函数本身的参数。
apply接收两个参数,第一个参数为函数上下文this,第二个参数为函数参数只不过是通过一个数组的形式传入的。
bind 接收多个参数,第一个是bind返回值返回值是一个函数上下文的this,不会立即执行。
23.聊聊es6的promise
Promise 是一种对异步操作的封装,可以通过独立的接口添加在异步操作执行成功、失败时执行的方法。主流的规范是 Promises/A+。
24.一个页面从输入URL到页面加载显示完成,这个过程中都发生了什么?
- 域名解析
- 发起TCP的3次握手
- 建立TCP连接后发起http请求
- 服务器端响应http请求,浏览器得到html代码
- 浏览器解析html代码,并请求html代码中的资源
25.var let const的区别
let
- let 只在let命令所在的代码块内有效。
- 不存在变量提升。
- 暂时性死区(只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。)
- 不允许在相同作用域内,重复声明同一个变量。
var
- var命令声明的,在全局范围内都有效
- 会发生变量提升
- 可以重复定义
- 没有块级作用域
const
- 声明一个只读的常量。一旦声明,常量的值就不能改变。
- 一旦声明变量,就必须立即初始化,
- 只在声明所在的块级作用域内有效。
- 不存在变量提升。
- 不可重复声明
26.数组去重(经常问)
var arr = [1, 1, 2, 3, 3, 3, 5, 6, 6, 6, 6, 3]
var newArry = [];
for (var i = 0; i < arr.length; i++) {
if(newArry.indexOf(arr[i])==-1)
{newArry.push(arr[i])
}
}
console.log(newArry,'wwwww')//es6方法
var arr = [1, 1, 2, 3, 3, 3, 5, 6, 6, 6, 6, 3]
var newArry = new Set(arr);
var filterArry = Array.from(newArry)
console.log(filterArry ,'sss')
//[1, 2, 3, 5, 6] "sss"27.获取并解析当前页面的url中的参数
function getUrlkey(url) {
var params = {} // 定义空对象
var arr = url.split('?') //分割字符串
console.log(arr,'ssss')
//["http://www.chenwenbo.info","key0=0&key1=1&key2=2"] "ssss
if (arr.length <= 1 )
return params;
arr = arr[1].split("&");
// 分割第二个数组下的字符串
console.log(arr,'xxx')
// ["key0=0", "key1=1", "key2=2"] "xxx
for (var i = 0 ; i < arr.length; i++) {
var a = arr[i].split("=");
console.log(a,'aaaav')
params[a[0]] = a[1];
}
return params;
}
var url = "http://www.chenwenbo.info?key0=0&key1=1&key2=2",
ps = getUrlkey(url);
console.log(ps);
//{key0: "0", key1: "1", key2: "2"}28.去掉变量 var str = ' Langer Beijing com! '中的所有空格
// 数组去重 es6 方法
var str = ' Langer Beijing com! '
NewStr = str.replace(/\s*/g,"");
console.log(NewStr,'NewStr')//八s*/g29.判断一个字符串出现个最多字符 并判断出个数
var obj = 'aaaabbbcadadvvvddssssss';
var i;
var tempObj = {};
for (var i = 0; i < obj.length; i++) {
var charAt = obj.charAt(i);
if (tempObj[charAt]) {
tempObj[charAt]++
} else {
tempObj[charAt] = 1
}
}
console.log(tempObj) //{a: 6, b: 3, c: 1, d: 4, v: 3}
var max = 0;//初始化一个最大数
var maxStr;
var obj1;
for (obj1 in tempObj) {
if (tempObj[obj1] > max) {
max = tempObj[obj1];
maxStr = obj1;
}
} console.log(maxStr + ':' + max); //a : 630.数组排序
var arr = [1, 2, 5, 6, 43, 111, 3]
for (var i = 0; i < arr.length; i++) {
for (var j = i; j < arr.length; j++){
if (arr[i] < arr[j]) {
max = arr[j];
arr[j] = arr[i];
arr[i] = max;
}
}
}
console.log(arr, '由大到小排序')
var min;
for (var i = 0; i < arr.length; i++) {
for (var j = i; j < arr.length; j++){
if (arr[i] > arr[j]) {
min = arr[j];
arr[j] = arr[i]
arr[i] = min;
}
}
}
console.log(arr,'由小到大')
31.箭头函数和普通函数的区别
- 箭头函数就是没有function关键字,而是一个类似箭头的函数:
- 箭头函数作为匿名函数,是不能作为构造函数的,不能使用new
- 箭头函数不绑定arguments,取而代之用rest参数…解决
- 箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值
- 箭头函数没有原型属性
- 箭头函数不能换行
32.实现数组的深拷贝
// es6 方法实现深拷贝
let arr1 = [1,2,3,4,5];
let arr2 = [...arr1];
console.log(arr2,'ssss')
// es5 slice 方法
var arr1 = [1,2,3,4,5];
var arr2 = arr1.slice(0)
console.log(arr2,'es6')
// es5 concat 方法
var arr1 = [1,2,3,4,5];
var arr2 = arr1.concat()
console.log(arr2,'ssss')33.get和post 区别
- 使用Get请求时,参数在URL中显示,而使用Post方式,则不会显示出来
- 使用Get请求发送数据量小,Post请求发送数据量大
- get请求需注意缓存问题,post请求不需担心这个问题
- 发送请求时,因为get请求的参数都在url里,所以send函数发送的参数为null,而post请求在使用send方法时,却需赋予其参数
- get请求是带着参数的,post请求的url则不带.
- Get请求的目的是给予服务器一些参数,以便从服务器获取列
34.求数组的最值
var arr = [3,43,23,45,65,90];
var max = Math.max.apply(null,arr);
console.log(max);35.数组的反转
var arr = [1,2,3,4];
var arr2 = arr1.reverse()
console.log(arr2)
// [1,2,3,4]36.求数组的和?
var arr1=[1,2,3,4,5,6,7,8,9];
var sum1=0;
for (var i=0;i<=arr1.length;i++) {if (typeof arr1[i]=="number") {
sum1+=arr1[i];
}}
console.log(sum1)37.for循环闭包组合(被问过2次)
1,对于一个基本的for循环,顺序输出变量值。
for(var i = 1; i < 4; i++){
console.log(i);//结果不多说了吧
}
// 0 1 2 3 2,如果for循环中有定时器,如下代码。
for (var i = 1; i < 4; i++) {
setTimeout(function() {
console.log(i);//3个4
}, 3000);
}
// 输出3个4 初衷想要3s后输出1,2,3。但是3s后,输出3个4。原因是定时器的异步执行,for循环的执行速度很快,当真正执行到函数体时,此时i早已变成4,所以结果不想而知。
3,如果要得到正确结果,就要引入闭包来保存变量i不被销毁。
for (var i = 1; i < 4; i++) {
(function(a) {
setTimeout(function() {
console.log(a);//操纵变量a,和i无关
}, 3000);
})(i)
}这样引入闭包,变量i保存下来,3s后函数体执行,输出1,2,3.
4,如果要实现,没隔3s输出一个数字,即,3s输出1,3s后再输出2...,就要对定时器时间设置
for (var i = 1; i < 4; i++) {
(function(a) {
setTimeout(function() {
console.log(a);
}, a*3000); //.....
})(i)
} 实际上,for循环很快,上述代码类似于同时启动3个定时器,只需要确保时间不一样即可。在此,时间分别是3s,6s,9s,由于同时启动,但是执行时间不同,各个时间间隔都是3s。巧妙地达到了目的。
38.什么是深拷贝,什么是浅拷贝
深拷贝,就是在对某个对象进行拷贝的时候,如果这个对象持有其他对象的引用,在拷贝的时候会将要拷贝的对象以及引用的对象,一起拷贝。
而浅拷贝只拷贝当前对象和持有的索引,不拷贝索引指向的对象。举个例子说明一下,比如当前有个列表a = [1,2,3], b = [3,4,a],[3,4,a]对象持有了[1,2,3]对象的引用,在对b进行深拷贝的时候,会将a对象一起拷贝一份,而浅拷贝的时候则不会。
a = [1,2,3]
b = [4,5,6,a]对b进行浅拷贝
c = copy.copy(b)这个时候对a对象进行修改,会影响c
a.append(8)
c
[4, 5, 6, [1, 2, 3, 8]]对b进行深拷贝之后,再对a进行修改,则不会影响到d
d = copy.deepcopy(b)
d
[4, 5, 6, [1, 2, 3, 8]]
a.append(66)
d
[4, 5, 6, [1, 2, 3, 8]38.将数组扁平化并去除其中重复数据,最终得到一个升序且不重复的数组
let newArr = [...new Set(arr.flat(Infinity)].sort((a,b)=>a-b)
console.log(newArr)
//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]39.使用 sort() 对数组 [3, 15, 8, 29, 102, 22] 进行排序,输出结果
let arr = [3, 15, 8, 29, 102, 22]
arr.sort((a, b)=>{
return a-b
})
// [3, 8, 15, 22, 29, 102]
40.给定两个数组,写一个方法来计算它们的交集
var nums1 = [1, 2, 2, 1], nums2 = [2, 2, 3, 4];
var newArr2 = nums1.filter((item) => {
return nums2.includes(item);
});
console.log(newArr2); // [2,2]41.如何把一个字符串的大小写取反(大写变小写小写变大写),例如 ’AbC' 变成 'aBc' 。
function processString (s) {
var arr = s.split('');
var new_arr = arr.map((item) => {
return item === item.toUpperCase() ? item.toLowerCase() : item.toUpperCase();
});
return new_arr.join('');
}
console.log(processString('AbC'));42.javascript让(a==1&&a==2&&a==3)为true
1、改写valueOf
let a = {
i : 1,
valueOf: function(){
return a.i++
}
}
console.log(a == 1 && a == 2 && a == 3);2、改写toString:
let a = {
toString: (function() {
let i = 1;
//闭包的特性之一:i 不会被回收
return function() {
return i++;
}
})()
}
console.log(a == 1 && a == 2 && a == 3); //true3、重写数组的 join 方法
let a = [1, 2, 3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3); //true43.闭包
// ====== 例子1 ======
for (var i = 0; i < 10; i++) {
setTimeout (function () {
console.log (i);
}, 1000);
}
// 10 10 10 ....
// ====== 例子2 ======
for (var i = 0; i < 10; i++) {
(function (e) {
setTimeout (function () {
console.log (e);
}, 1000);
})(i);
}
// 1 2 3 4 5 6 7....
// ====== 例子3 ======
for (var i = 0; i < 10; i++) {
setTimeout (function (i) {
console.log (i);
}, 1000);
}
// undefined undefined undefined ...
// ====== 例子4 ======
var x = 'global'
function test1() {
console.log(x)
}
function test2() {
x = 'local'
test1()
}
test2()
// local
// ====== 例子5 ======
var x = 'global'
function test1() {
console.log(x)
}
function test2() {
var x = 'local'
test1()
}
test2()44.作用域
- 分为:全局作用域(定义在函数外部的变量)和局部作用域(定义在函数内部的变量),每个函数在被调用时都会创建一个新的域。ECMAScript 6引入了let和const关键字,利用let和const可以形成块级作用域。
- 块语句(if, switch, for, while)不会创建新的作用域,定义的变量保存在已经存在的作用域中,let和const支持在局部作用域块语句中声明
if (true) {
var a = 1;
let b = 2;
const c =3;
}
console.log(a) // 1
console.log(b) // Uncaught ReferenceError: b is not defined
console.log(c) // Uncaught ReferenceError: c is not defined
作用域链:作用域链是在变量对象之后创建的,作用域链用于解析变量,当变量被解析时,javascript先从代码嵌套的最内层开始找,如果内层没有找到,会转到上一层父级作用域查找,直到找到该变量
45.节流和防抖
节流:高频事件触发n秒内执行一次,如果这个时间点内触发多次函数,只有一次生效。
function throttle(fn) {
var flag = true
return function() {
if (!flag) return;
flag = false;
setTimeout(function () {
fn()
flag = true
}, 1000)
}
}防抖:高频事件触发n秒之后执行,如果n秒之内再次被触发,重新记时。
function debounce(fn) {
var timeout = null;
return function() {
clearTimeout(timeout)
timeout = setTimeout(function (){
fn()
}, 1000)
}
}46.get和post区别
- 最直观的区别get把参数包含在URL中,post通过request body传递参数,相对于get比较安全
- get请求URL传参有长度限制,post没有
- get在浏览器回退是无害的,post会再次提交请求
- get请求会被浏览器主动缓存,post不会
- get和post报文格式不同
- get请求是幂等性的,而post请求不是(新增和删除数据一般不用post请求就是这个原因)
47..Js的事件委托是什么,原理是什么
- 通俗点说将元素事件委托给他的父级或者更外级来处理
- 事件委托是利用冒泡机制来实现的(事件冒泡:事件由具体的元素接受,然后逐渐向上传播到不具体的节点)
// 每个列表点击弹出内容
// 不使用事件委托需要给每个列表添加点击事件(消耗内存,不灵活,添加动态元素时需要重新绑定事件)
这里不做介绍
<ul id="myLink">
<li id="1">aaa</li>
<li id="2">bbb</li>
<li id="3">ccc</li>
</ul>
// 使用事件委托(减少内存占用,提升性能,动态添加元素无需重新绑定事件)
var myLink = document.getElementById('myLink');
myLink.onclick = function(e) {
var e = event || window.event;
var target = e.target || e.srcElement;
if(target.nodeName.toLowerCase() == 'li') {
alert(target.id + ':' + target.innerText);
}
};48.什么是原型链
- 原型:每个javascript创建的时候都会关联另一个对象,这个对象就是原型,对象会从原型继承属性
- 构造函数可以通过prototype去寻找他关联的原型,A.prototype就是它关联的原型对象,原型对象可以通过构造器constructor来寻找与自身关联的构造函数
function A () {
}
A.prototype.constructor === A //true- 原型链:原型链是由原型对象组成,每个对象都有__proto__属性,指向该构造函数的原型,__proto__将对象连接起来组成了原型链
- 原型链查找机制:当查找对象的属性时,如果实例对象不存在该属性,沿着原型链向上一级查找,直到找到object.prototype(也就是对象原型object.prototype为null),停止查找到返回undefined
function A () {
}
new A().__proto__ === A.prototype //true原型上的属性和方法被实例共享
function A () {
}A.prototype.name = 'a'
var a = new A()
var b = new A()
a.name === b.name // true
a.__proto__.name === b.__proto__.name // trueinstanceOf原理:instamceOf可以判断实例对象的__proto__属性与构造函数的prototype是不是同一地址(如果网页中有多个全局环境就会不准确)
function _instanceOf(obj, type) {
var obj = obj.__proto__
var type = type.prototype
while(true) {
if (obj == null) {
return false
}
if (obj == type) {
return true
}
obj = obj.__proto__
}
}
var a = [1, 2, 3]
_instanceOf(a, Array)49.深拷贝和浅拷贝
浅拷贝只是复制引用,新旧对象共享一块内存,一般把第一层拷贝到一个对象上,改变其中一个另一个也会改变
var obj = {
name: "a",
age: 18,
arr: [1, 2]
};
function shallowCopy(obj) {
var newObj = {};
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
// 过滤掉原型上的属性
newObj[prop] = obj[prop];
}
}
return newObj;
}
var obj1 = shallowCopy(obj);
var obj2 = obj;
obj1.name = "b";
obj2.age = 20;
obj2.arr[0] = 3;
obj1.arr[0] = 4;
console.log(obj); // {name: "a", age: 20, arr: [4, 2]}
console.log(obj1); // {name: "b", age: 18, arr: [4, 2]}
console.log(obj2); // {name: "a", age: 20, arr: [4, 2]}我们通过浅拷贝得到obj1,改变obj1的name,obj不会发生改变,通过赋值得到obj2,obj2改变age值obj的值也会被改变。说明赋值得到的对象只是改变了指针,浅拷贝是创建了新对象。
我们通过obj2和obj1都改变的值,发现obj,ob1,obj2都发生了改变,所以无论是浅拷贝还是赋值都会改变原始数据(浅拷贝只拷贝了一层对象的属性)
深拷贝:复制并创建一个一摸一样的对象(递归复制了所有层级),不共享内存,改变其中一个另一个不会改变
var obj = {
name: "a",
age: 18,
arr: [1, 2]
};
function copy(obj) {
var newobj = Array.isArray(obj) ? [] : {};
if (typeof obj !== "object") {
return;
} for (var i in obj) {
if (obj.hasOwnProperty(i)) {
newobj[i] = typeof obj[i] === "object" ? copy(obj[i]) : obj[i];
}
} return newobj;
}
var copyObj = copy(obj);
copyObj.arr[0] = 3;
console.log(copyObj);
//{name: "a", age: 20, arr: [3, 2]}
console.log(obj);
//{name: "a", age: 20, arr: [1, 2]}50.实现继承
- 原型继承
- 构造继承
- 实例继承
- call/apply继承(组合继承)
- ES6 使用class extends继承
51.如何判断一个对象是数组
面试官希望的答案: Object.prototype.toString.call([]) 返回 "[object Array]"
扩展答案
- [].slice (能力判断 )
- [] instanceof Array(类型判断)
- [].__proto__ === Array.prototype
在考虑兼容性的情况下可以⽤toString的⽅法 if(!Array.isArray){ Array.isArray = function(arg){ return Object.prototype.toString.call(arg)==='[object Array]' } } - Array.isArray([])
//es6中加⼊了新的判断⽅法 if(Array.isArray(value)){ return true; }
52.谈谈你对原型链的理解?
原型对象:绝⼤部分的函数(少数内建函数除外)都有⼀个 prototype 属性,这个属性是原型对象⽤来创建新对象实例,⽽所有被创建的对象都会共享原型对象,因此这些对象便可以访问原型对象的属性。
⽤法: object.hasOwnProperty( propertyName ) hasOwnProperty()
函数的返回值为 Boolean 类型。如果对象 object 具有名称为 propertyName 的属性,
则返 回 true ,否则返回 false 。var person = {
name: "Messi",
age: 29,
profession: "football player"
};
console.log(person.hasOwnProperty("name")); //true
console.log(person.hasOwnProperty("hasOwnProperty")); //false
console.log(Object.prototype.hasOwnProperty("hasOwnProperty")); //true
原型链:原因是每个对象都有 __proto__ 属性,此属性指向该对象的构造函数的原型。对象可以通过 __proto__ 与上游的构造函数的原型对象连接起来,⽽上游的原型对象也有⼀个 __proto__ ,这样就形成了原型链。
53.那么箭头函数的this指向哪⾥?
// ES6const obj = {getArrow()
{return () => {console.log(this === obj);
};
}
}
// ES5,由 Babel 转译
var obj = {getArrow: function getArrow()
{var _this = this;return function ()
{console.log(_this === obj);
};
}}
54.async/await是什么?
55.async/await相⽐于Promise的优势?
Promise传递中间值⾮常麻烦,⽽async/await⼏乎是同步的写法,⾮常优雅
调试友好,Promise的调试很差,由于没有代码块,你不能在⼀个返回表达式的箭头函数中设置断点,如果你在⼀个.then代码块中使⽤调试器的步进(step-over)功能,调试器并不会进⼊后续的.then代码块,因为调试器只能跟踪同步代码的『每⼀步』。
56.JavaScript的参数是按照什么⽅式传递的?
var a = 1;function test(x) {
x = 10;
console.log(x);
}
test(a); // 10
console.log(a); // 1虽然在函数 test 中 a 被修改,并没有有影响到 外部 a 的值,基本类型是按值传递的.
复杂类型按引⽤传递
var a = { a: 1, b: 2};
function test(x) {
x.a = 10;
console.log(x);
}
test(a); // { a: 10, b: 2 }
console.log(a); // { a: 10, b: 2 }可以看到,在函数体内被修改的 a 对象也同时影响到了外部的 a 对象,可⻅复杂类型是按引⽤传递的.
var a = { a: 1, b: 2 };
function test(x) {
x = 10;
console.log(x);
}test(a);
// 10
console.log(a); // { a: 1, b: 2 }
外部的 a 并没有被修改,如果是按引⽤传递的话,由于共享同⼀个堆内存, a 在外部也会表现为 10 才对. 此时的复杂类型同时表现出了 按值传递 和 按引⽤传递 的特性.
57.什么是变量提升?
console.log(a) // undefined
var a = 1
function b() {
console.log(a)
}
b() // 1
var a = undefined
console.log(a) // undefined
a = 1
function b() {
console.log(a)
}
b() // 1第⼆步就是执⾏,因此js引擎⼀⾏⼀⾏从上往下执⾏就造成了当前的结果,这就叫变量提升。
58.JavaScript的作⽤域链理解?
59.ES6模块与CommonJS模块有什么区别?
60.为什么0.1+0.2为什么不等于0.3?
61.类型转换的规则有哪些?
62.map和set区别
Set类似于数组,但是它里面每一项的值是唯一的,没有重复的值,Set是一个构造函数,用来生成set的数据结构。
let s = new Set();
let arr = [2, 3, 5, 4, 5, 2, 2];
arr .forEach(item => arr.add(item)); //向set添加重复的值for (let i of s) {
console.log(i);
}
// 2 3 5 4 结果set不会添加重复的值
Map类似于对象,也是键值对的集合,但是“键”的范围不限制于字符串,各种类型的值(包含对象)都可以当作键。Map 也可以接受一个数组作为参数,数组的成员是一个个表示键值对的数组。注意Map里面也不可以放重复的项。
let map = new Map([['js','react']]);
map.set('js','react');//看看是否可以放重复的项
map.set('javaScript','vue');
console.log(map)//Map {'js' => 'react','javaScript' => 'vue'} 不可以放重复项