北京MedPeer凉经

一、请说出三种不同含义的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); //<div id="foo"></div>
};
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<arr.length;i++){
	var key = arr[i].split('=')[0]
	var value = arr[i].split('=')[1]
	obj[key]=value
}
var str = prompt('请输入键')
console.log(obj[str])

三、说一下你对原型链的理解、应用场景、以及下面代码报错原因

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还没开始,不过老本已经不够吃了,需要加油啊,很多基础的东西都忘记了。还有一部分问题,我打算放到其他复习文章里面,就不在这里说了

    

    

 

原文地址:https://www.cnblogs.com/spoem/p/13255459.html