1.闭包和作用域:
https://www.cnblogs.com/chengzp/p/scopechain.html
一般来说,只有函数作用域和块作用域,es6有块作用域
闭包就是能够读取其他函数内部变量的函数
function F1(){
var a = 100
//返回一个函数 (函数作为返回值)
return function (){
console.log(a)
}
}
//f1得到一个函数
var f1 = F1()
var a = 200
f1()
例题:创建10个<a>标签,点击的时候输出对应的序号
看到这道题,最直接的想法或许就i是这样的
//错误代码演示var i,a
for (i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '<br />'
a.addEventListener('click',function(e){
e.preventDefault()
console.log(i)
})
document.body.appendChild(a)
}
但这是不对的。
可以看到,我对着左边的标签一顿乱点,结果打印的都是10.为什么呢
因为上例代码都是同步执行的,在页面加载的一瞬间,for循环已经执行完毕,我再去点标签的时候,i的值已经是10了,所以不管我点第几个标签,打印的都会是10。
那怎么解呢?
这么解
for (var i = 0; i < 10; i++) {
(function(i){
a = document.createElement('a')
a.innerHTML = i + '<br />'
a.addEventListener('click',function(e){
e.preventDefault()
console.log(i)
})
document.body.appendChild(a)
})(i)
}
2.this的指向问题:
es6箭头函数解决了this造成的一些问题
1.全局环境下,this 始终指向全局对象(window), 无论是否严格模式;
2.函数直接调用this
(1)非严格模式下,this 默认指向全局对象window
(2)严格模式下,this 默认为undefined
3.es6箭头函数里的this
指的是定义这个函数时外层代码的this
,这句话可以从两个方面理解:(
- es6箭头函数没有自己的
this
- es6箭头函数里的
this
是外层代码(定义时,非执行时)this
的引用
)
var Animal = function() {
this.name = "Animal"; // 下面那一行的this指向的是这外面的this
this.speak = (name,words) => {
console.log(this.name + ' is saying ' + words + '.');
}
}
var cat = new Animal();
cat.speak(22,"miao ~"); // Animal is saying miao ~.
var speak = cat.speak;
speak(33,"miao ~"); // Animal is saying miao ~.
4.this的取值依赖于函数被谁调用
1.如果是使用new关键字来调用函数,那么函数内部的this就是一个全新的对象。
2.如果使用apply、call或者bind来调用一个函数,函数内部的this就将指向传入的第一个参数。(注:使用这几个方法可以改变this的指向)
3.如果函数被作为一个方法进行调用,比如:obj.method() --- 那么this就该函数的调用者。(注:样例中的obj)
3.箭头函数和普通函数的区别
(1)箭头函数的this在定义时确定(父级作用域),而非运行时(普通函数在运行时确定)
(2)箭头函数不能当作构造函数通过new来新建实例(追问:为什么不能new? 当时回答应该和new的过程有关,但是具体没答上来,后来百度了一下,大概是因为箭头函数没有自己的prototype)
(3)没有argumnets对象
(4)没有prototype
(5)不能用yield命令,因此箭头函数不能用做Generator函数
4."use strict"严格模式:
1.变量使用前要先声明
2.不能对变量执行delete
3.禁止this关键字指向全局对象
4.禁用eval()
5.对象的属性名不能重复
6.不能修改arguments
7.不能在函数内定义arguments变量
8.不能使用arugment.caller和argument.callee。因此如果你要引用匿名函数,需要对匿名函数命名。
5.setTimeout(foo,0)可以写成foo()吗?
那如果是setTimeout( foo, 0 )呢?0毫秒哎,foo是不是会马上执行?
不会!不会!不会! setTimeout( foo, 0 )的意思是,把foo在0ms内推入任务队列,而浏览器会继续执行执行栈中的事件,当执行栈空了,才会把任务队列中的foo事件推入执行栈执行。如果当前执行栈的事件很多,foo还是得继续等着哦,等到执行栈中的事件全部执行完了,foo才能进入到执行栈中执行。所以不是在0毫秒后就立即执行foo的意思。
那么setTimeout( foo, 0 )有什么作用呢?
同步变异步,降低foo事件优先级,把它放到后面执行。
6.宏任务和微任务
首先是event loop,同步任务和异步任务在不同的线程执行,异步任务的回调函数放进事件队列中。
当主线程的任务执行完了(执行栈空了),JS会去询问事件队列
事件队列中分为宏任务和微任务,(实际上宏任务和微任务处于两条不同队列)先执行宏任务,执行完后若发现有微任务则执行微任务(每执行完一个宏任务都要把现存的微任务执行完)
宏任务
微任务
7.浅拷贝和深拷贝有什么区别?分别有什么方式?
https://juejin.im/post/6844903807705415687
https://juejin.im/post/6844903833223561229
当对象的属性值为基本数据类型,那么拷贝的就是基本数据类型的值,如果对象的属性值为引用数据类型,那么拷贝的就是内存地址,上面的代码可以用下图更形象的解释:
浅拷贝
1.Object.assign()
Object.assign()
方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign()
进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
var obj = { a: {a: "kobe", b: 39} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "wade";
console.log(obj.a.a); // wade
注意:当object只有一层的时候,是深拷贝
let obj = {
username: 'kobe'
};
let obj2 = Object.assign({},obj);
obj2.username = 'wade';
console.log(obj);//{username: "kobe"}
2. Array.prototype.concat()
let arr = [1, 3, {
username: 'kobe'
}];
let arr2=arr.concat();
arr2[2].username = 'wade';
console.log(arr);
3. Array.prototype.slice()
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);
深拷贝(指向不同的地址)
8.addEventListener()
第三个参数是true时,在事件捕获阶段执行,是false时,在时间冒泡阶段执行
事件冒泡的过程
9.JS的垃圾回收机制
垃圾回收算法:“标记-清除算法”
- 垃圾回收器获取根并“标记”(记住)它们
- 然后它访问并“标记”所有来自它们的引用。
- 然后它访问标记的对象并标记它们的引用。所有被访问的对象都被记住,以便以后不再访问同一个对象两次。
- 以此类推,直到没有未访问的引用(可以从根访问)为止。
- 除标记的对象外,所有对象都被删除。
什么是垃圾
一般来说没有被引用的对象就是垃圾,就是要被清除, 有个例外如果几个对象引用形成一个环,互相引用,但根访问不到它们,这几个对象也是垃圾,也要被清除。
如何检垃圾
使用“标记-清除算法”
10.var和let、const的区别
1.var声明变量存在变量提升,let和const不存在变量提升
2.let、const都是块级局部变量 顾名思义,就是只在当前代码块起作用
3.同一作用域下let和const不能声明同名变量,而var可以
11.AJAX
JS原生AJAX
function request() {
var request;
if(window.XMLHttpRequest){
request=new XMLHttpRequest();
}else{
request=new ActiveXObject();
}
request.onreadystatechange=function(){
if (xmlhttp.readyState===4 && xmlhttp.status===200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
request.open('GET',"/try/ajax/demo_get.php?t=" + Math.random(),true)
//使用post的情况
// xmlhttp.open("POST","/try/ajax/demo_post2.php",true);
// xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
// xmlhttp.send("fname=Henry&lname=Ford");
request.send()
}
JQ的AJAX
$("button").click(function(){
$.ajax({url:"demo_test.txt",success:function(result){
$("#div1").html(result);
}});
});
//使用$.post来提交post请求
$("input").keyup(function(){
txt=$("input").val();
$.post("demo_ajax_gethint.asp",{suggest:txt},function(result){
$("span").html(result);
});
});
//使用$.get来提交get请求
$("button").click(function(){
$.get("demo_test.html",{ name:"Donald", town:"Ducktown" },function(data,status){
alert("Data: " + data + "nStatus: " + status);
});
});
12.原型和原型链
面试官:谈谈你对 JS 原型和原型链的理解?
候选人:JS 原型是指为其它对象提供共享属性访问的对象。在创建对象时,每个对象都包含一个隐式引用指向它的原型对象或者 null。
原型也是对象,因此它也有自己的原型。这样构成一个原型链。
面试官:原型链有什么作用?
候选人:在访问一个对象的属性时,实际上是在查询原型链。这个对象是原型链的第一个元素,先检查它是否包含属性名,如果包含则返回属性值,否则检查原型链上的第二个元素,以此类推
面试官:那如何实现原型继承呢?
候选人:有两种方式。一种是通过 Object.create 或者 Object.setPrototypeOf 显式继承另一个对象,将它设置为原型。
另一种是通过 constructor 构造函数,在使用 new 关键字实例化时,会自动继承 constructor 的 prototype 对象,作为实例的原型。
在 ES2015 中提供了 class 的风格,背后跟 constructor 工作方式一样,写起来更内聚一些。
13.Arguments
是函数的参数,不建议在es6中使用
function func1(a, b, c) {
console.log(arguments[0]);
// expected output: 1
console.log(arguments[1]);
// expected output: 2
console.log(arguments[2]);
// expected output: 3
}
func1(1, 2, 3);
14.call()、apply()、bind()
都是用来定义对象函数中this指向的对象的
比如
obj.myFun.call(db); // 德玛年龄 99
obj.myFun.apply(db); // 德玛年龄 99
obj.myFun.bind(db)(); // 德玛年龄 99
以上出了 bind 方法后面多了个 () 外 ,结果返回都一致!
由此得出结论,bind 返回的是一个新的函数,你必须调用它才会被执行。(因为call和apply都是函数调用时使用,而bind是函数调用时使用)
对比三个函数传参情况
obj.myFun.call(db,'成都','上海'); // 德玛 年龄 99 来自 成都去往上海
obj.myFun.apply(db,['成都','上海']); // 德玛 年龄 99 来自 成都去往上海
obj.myFun.bind(db,'成都','上海')(); // 德玛 年龄 99 来自 成都去往上海
obj.myFun.bind(db,['成都','上海'])(); // 德玛 年龄 99 来自 成都, 上海去往 undefined
从上面四个结果不难看出:
call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:
call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,'成都', ... ,'string' )。
apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,['成都', ..., 'string' ])。
bind 除了返回是函数以外,它 的参数和 call 一样。
当然,三者的参数不限定是 string 类型,允许是各种类型,包括函数 、 object 等等!
15.扩展/剩余运算符
扩展运算符
剩余运算符
剩余运算符可以和数组的解构赋值一起使用,但是必须放在最后一个,因为剩余运算符的原理其实是利用了数组的迭代器,它会消耗3个点后面的数组的所有迭代器,读取所有迭代器生成对象的value属性,剩运算符后不能在有解构赋值,因为剩余运算符已经消耗了所有迭代器,而数组的解构赋值也是消耗迭代器,但是这个时候已经没有迭代器了,所以会报错
这里first会消耗右边数组的一个迭代器,...arr会消耗剩余所有的迭代器,而第二个例子...arr直接消耗了所有迭代器,导致last没有迭代器可供消耗了,所以会报错,因为这是毫无意义的操作
剩余运算符和扩展运算符的区别就是,剩余运算符会收集这些集合,放到右边的数组中,扩展运算符是将右边的数组拆分成元素的集合,它们是相反的
16.for in 和for of
使用 for in 循环遍历对象的属性时,原型链上的所有属性都将被访问:
Object.prototype.bar = 10;// 修改Object.prototype
var obj={"name":"wjy","age":26,"sex":"female"};//定义一个object对象
var keys=[];//定义一个数组用来接受key
var values=[];//定义一个数组用来接受value
for(var key in obj){
keys.push(key);
values.push(obj[key]);//取得value
}
alert("keys is :"+keys+" and values is :"+values);
//keys is : name,age,sex,bar and values is : wjy,26,female,10
推荐总是使用 hasOwnProperty 方法, 这将会避免原型对象扩展带来的干扰:
function allpro(obj){
var keys=[];
var values=[];
for(var key in obj){
//只遍历对象自身的属性,而不包含继承于原型链上的属性。
if (obj.hasOwnProperty(key) === true){
keys.push(key);
values.push(obj[key]);
}
}
alert("keys is :"+keys+" and values is :"+values);
}
Object.prototype.bar = 1;// 修改Object.prototype
var o={"name":"wjy","age":26,"sex":"female"};//定义一个object对象
allpro(o);
//keys is : name,age,sex and values is: wjy,26,female
for of只会迭代当前对象的迭代器
17.RegExp
https://www.runoob.com/jsref/jsref-obj-regexp.html
18.跨域
http://www.ruanyifeng.com/blog/2016/04/cors.html
实现方式参考https://blog.csdn.net/qq_38128179/article/details/84956552
什么是跨域
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
【1】无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
【2】无法接触非同源网页的 DOM
【3】无法向非同源地址发送 AJAX 请求
解决方案
【1】设置document.domain解决无法读取非同源网页的 Cookie问题
【2】跨文档通信 API:window.postMessage()
【3】JSONP
jsonp的介绍:https://blog.csdn.net/u014660247/article/details/80912553
【4】CORS
CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。
1、普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
2、带cookie跨域请求:前后端都需要进行设置
实现:https://blog.csdn.net/qq_38128179/article/details/84956552