一、请说出三种不同含义的this使用场景
1.作为对象方法调用
在 javascript 中,函数也是对象,因此函数可以作为一个对象的属性,此时该函数被称为该对象的方法,在使用这种调用方式时,this 被自然绑定到该对象
var test = {
a:0,
b:0
get:function(){
return this.a;
}
}
2.作为全局变量使用
函数也可以直接被调用,此时 this 绑定到全局对象。在浏览器中,window 就是该全局对象。比如下面的例子:函数被调用时,this 被绑定到全局对象,接下来执行赋值语句,相当于隐式的声明了一个全局变量。
var x = 10;
function foo() {
console.log(this); //Window
console.log(this.x); //10
}
foo();
3.作为构造函数调用
javascript 支持面向对象式编程,与主流的面向对象式编程语言不同,javascript 并没有类(class)的概念,而是使用基于原型(prototype)的继承方式。相应的,javascript 中的构造函数也很特殊,如果不使用 new 调用,则和普通函数一样。作为又一项约定俗成的准则,构造函数以大写字母开头,提醒调用者使用正确的方式调用。如果调用正确,this 绑定到新创建的对象上。
function Foo() {
this.x = 10;
console.log(this); //Foo {x:10}
}
var foo = new Foo();
console.log(foo.x); //10
4.在call、apply、bind中调用
当一个函数被 call、apply 或者 bind 调用时,this 的值就取传入的对象的值。
var obj = {
x: 10,
};
function foo() {
console.log(this); //{x: 10}
console.log(this.x); //10
}
foo.call(obj);
foo.apply(obj);
foo.bind(obj)();
5.作为DOM节点
在一个 html dom 事件处理程序里,this 始终指向这个处理程序所绑定的 html dom 节点
function Listener() {
document.getElementById('foo').addEventListener('click', this.handleClick); //这里的 this 指向 Listener 这个对象。不是强调的是这里的 this
}
Listener.prototype.handleClick = function(event) {
console.log(this); //
};
var listener = new Listener();
document.getElementById('foo').click();
6.箭头函数中的this
箭头函数内部的 this 是词法作用域,由上下文确定。
var obj = {
x: 10,
foo: function() {
var fn = () => {
return () => {
return () => {
console.log(this); //Object {x: 10}
console.log(this.x); //10
};
};
};
fn()()();
},
};
obj.foo();
以上是六种this的使用环境,原文链接
二、编程题
实现一个获取当前页面链接search值的函数。(如下例子中,输入key1输出value1)例:’http://test.com/index?key1=value1&key2=value2’
思路:需要根据key找到value,首先将?后面拼接的参数提取出来,然后将其转为对象形式,再按键取值
实现:使用DOM中location对象的属性+substr+split方法获取键值对数组,再遍历数组将里面的key1=value1通过等号分开,再存入对象中
var arr = window.location.search.substr(1).split("&")
var obj={}
for(var i=0;i
三、说一下你对原型链的理解、应用场景、以及下面代码报错原因
function Dog(name) {
this.name = name
}
Dog.bark = function() {
console.log(this.name + ' says woof')
}
let fido = new Dog('fido');
fido.bark()
理解:在javascript里面,每个对象都有一个prototype属性,指向它的原型对象.这个原型对象里面同时还有自己的原型,原型一环扣一环,直到某个对象的原型为null,这一级一级的链结构就是原型链。
应用场景:js通过原型链实现函数或对象的继承
报错原因:fido这个对象并没有bark方法。代码中虽然给dog对象绑定了bark方法,但是绑定的是静态方法,调用形式只能通过dog.bark去调用。Fido并没有继承这个方法,所以会报错。
Ps:如果需要fido.bark()不报错。可以在dog函数的内部写this.bark=function(){}或者在绑定bark方法时,使用Dog.prototype.bark=function(){}
四、什么是事件委托、简述它的作用和原理
概念:事件委托就是事件目标(子元素)不处理事件,把事件委托给父元素去处理。
作用:1、减少页面绑定事件的数量,提高页面的性能
2、可以灵活的处理子节点动态变化的场景,无论子节点增加还是减少,事件都无需重新绑定
原理:利用事件冒泡,将事件加在父元素或者祖先元素上,触发该事件。
五、下面的代码执行结果是什么,请说明理由
function greet(person) {
if (person == { name: 'amy' }) {
return 'hey amy'
} else {
return 'hey arnold'
}
}
greet({ name: 'amy' })
执行结果在控制台打印出'hey arnold'。greet方法传入一个对象时,与内部的对象{ name: 'amy' }进行==判断,尽管这两个对象的结构、键值相同,但是他们的地址不同,所以返回flase,执行了else语句里面的代码,返回了'hey arnold'
六、写出下面代码执行后正确顺序的输出结果以及原因解析
var a=1;
function fun1(){
console.log(a);
}
function fun2(){
console.log(a);
var a=2;
console.log(a);
}
fun1();
fun2();
console.log(a);
原因:首先执行fun1()函数,因为已经有全局变量a=1,所以打印出1,然后执行fun2()函数,函数内部存在变量提升,函数内部实际执行顺序:
var a;
console.log(a)//undefined;
a=2;
conosle.log(a)//2
所以打印出undefined和2,最后在执行console.log(a),这里仍然打印出的是全局变量a的值,所以是1
七、写出下面代码执行后正确顺序的输出结果以及原因解析
var a=1;
console.log(1);
setTimeout(function () {
console.log(2);
},0);
function fun() {
console.log(3);
setTimeout(function () {
console.log(4);
},0);
for (var i=0;i<10000;i++){
a++;
}
console.log(5);
return a;
};
console.log(6);
console.log(fun());
正确输出顺序1 6 3 5 10001 2 4
原因:
按照顺序执行代码,先打印出1,然后遇到第一个setTimeout,会将其存放到异步队列,等待主线程执行完成之后才会执行。然后打印6,再执行fun函数。Fun函数内部首先打印出3,然后遇到第二个setTimeout,也将其放入异步队列。,再执行for循环,for循环内部a的最终值是10001。然后打印5,再返回a,打印a的值为10001。此时主线程执行完毕,执行先前存放的setTimeout。又因为队列是先进先出,所以先打印2再打印4。
八、JS的基本数据类型有哪些
7种:Boolean Null Undefined Number String Symbol(ES6) BigInt (ES10)
九、vue的双向数据绑定原理
首先要对数据进行劫持监听,vue设置了一个监听器observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者watcher看是否需要更新。因为订阅者是有很多个,vue设置了一个消息订阅器dep来专门收集这些订阅者,然后在监听器observer和订阅者watcher之间进行统一管理的。接着,我们还需要有一个指令解析器compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者watcher,并替换模板数据或者绑定相应的函数,此时当订阅者watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。(后面复习vue的时候我会单独写一篇文章解释,这个就先放到这里,不全面!)
十、原生js如何实现双向数据绑定
这篇文章非常详细了,传送门
十一、如何监听多个promise的执行状态以及promise的应用场景
使用promise.all()方法,promise.all(iterable) 方法返回一个 promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果
应用场景:js异步编程的新方案,解决了以往的回调嵌套回调出现回调地狱的问题
十二、DOM节点的增删改查,getElementBy和querySelector获取DOM节点的区别
DOM的增删改查就不再赘述了,传送门。详细的说一下getElementBy和querySelector的区别
1.W3C标准
queryselectorall 属于 w3c 中的 selectors api 规范 。而 getelementsby 系列则属于 w3c 的 dom 规范 。(了解)
2.浏览器兼容
queryselectorall 已被 ie 8+、ff 3.5+、safari 3.1+、chrome 和 opera 10+ 良好支持 。getelementsby 系列,以最迟添加到规范中的 getelementsbyclassname 为例,ie 9+、ff 3 +、safari 3.1+、chrome 和 opera 9+ 都已经支持该方法了。
3.接收参数
querySelectorAll 方法接收的参数是一个 CSS 选择符。而 getElementsBy 系列接收的参数只能是单一的className、tagName 和 name。代码如下:
var c1 = document.querySelectorAll('.b1 .c');
var c2 = document.getElementsByClassName('c');
var c3 = document.getElementsByClassName('b2')[0].getElementsByClassName('c');
需要注意的是,queryselectorall 所接收的参数是必须严格符合 css 选择符规范的。所以下面这种写法,将会抛出异常。(css 选择器中的元素名,类和 id 均不能以数字为开头。)代码如下:
try {
var e1 = document.getElementsByClassName('1a2b3c');
var e2 = document.querySelectorAll('.1a2b3c');
} catch (e) {
console.error(e.message);
}
console.log(e1 && e1[0].className);
console.log(e2 && e2[0].className);
4.返回值(获取的结点)
大部分人都知道,queryselectorall 返回的是一个static node list,而 getelementsby 系列的返回的是一个Live Node List。
// Demo 1
var ul = document.querySelectorAll('ul')[0],
lis = ul.querySelectorAll("li");
for(var i = 0; i < lis.length ; i++){
ul.appendChild(document.createElement("li"));
}
// Demo 2
var ul = document.getElementsByTagName('ul')[0],
lis = ul.getElementsByTagName("li");
for(var i = 0; i < lis.length ; i++){
ul.appendChild(document.createElement("li"));
}
因为 demo 2 中的 lis 是一个动态的 node list, 每一次调用 lis 都会重新对文档进行查询,导致无限循环的问题。 而 demo 1 中的 lis 是一个静态的 node list,是一个 li 集合的快照,对文档的任何操作都不会对其产生影响。
至于为什么queryselectorall拿到的是一个快照,源自W3C的规定【the nodelist object returned by the queryselectorall() method must be static ([dom], section 8)】
ps:queryselectorall 的返回值是一个静态的 nodelist 对象,而 getelementsby 系列的返回值实际上是一个 htmlcollection 对象 。nodelist 对象会包含文档中的所有节点,如 element、text 和 comment 等。 htmlcollection 对象只会包含文档中的 element 节点。
5.性能
因为queryselectorall返回一个静态的nodelist(深克隆) getelementby系列返回一个动态的实时变化的nodelist(htmlcollection)(浅克隆,每次都返回一个指针) 所以queryselectorall会降低性能
十三、总结
由于一直没来得及复习,js还没开始,不过老本已经不够吃了,需要加油啊,很多基础的东西都忘记了。还有一部分问题,我打算放到其他复习文章里面,就不在这里说了