美文网首页
javascript基本知识集合

javascript基本知识集合

作者: fengcol | 来源:发表于2019-02-20 10:46 被阅读0次

    一.ES基础知识点

    1.javascript基本类型有几种?引用类型有哪些?用什么方法可以区分他们?

    答:js基本类型有6种,分别是String,Number,Boolean,Null,Undefined,Symbol(es6);还有一种比较特殊的基本类型Object;引用类型有Array,Function,Date,Object等等。

    基本类型都是按值传递,而引用类型是按引用传递。例如

    var a = 10;
    var b = a;
    b = 5;
    console.log(a);//10
    console.log(b);//5
    

    上述代码因为a属于基本类型,是按值传递的,所以即使b改变了,a并不会发生变化。

    var obj1 = {
      name:'zhang san',
      age:20
    };
    var obj2 = obj1;
    obj2.age = 10;
    console.log(obj2.age);//10
    console.log(obj1.age);//10
    

    因为obj1是引用类型,当obj1被创建时,会单独创建一个内存,obj1会指向该内存区域;当obj2=obj1时,此时obj2同样会指向该内存区域,当obj2发生改变时,内存区域发生改变,从而obj1也会发生改变。

    基本类型可通过typeof进行判断;但是null值除外,因为typeof(null)会返回Object; typeof函数会返回Function。

    引用类型可通过instanceof()函数来判断;比如

    var a = new Date();
    a instanceof Date;//true
    

    2.proto和prototype的区别?

    答:所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(null除外)

    • 所有的引用类型(数组、对象、函数),都有一个**proto**属性,属性值是一个普通的对象

    • 所有的函数,都有一个prototype属性,属性值也是一个普通的对象

    • 所有的引用类型(数组、对象、函数),**proto**属性值指向它的构造函数的prototype属性值。

    //要点一,自由扩展性;
    var obj = {}; obj.a = 100;
    var arr = []; arr.a = 100;
    function fn(){
    
    }
    fn.a = 100;
    //要点二:_proto_
    console.log(obj._proto_);
    console.log(arr._proto_);
    console.log(fn._proto_);
    //要点三:函数有prototype
    console.log(fn.prototype);
    //要点四:引用类型的_proto_属性值指向其构造函数的prototype
    console.log(obj._proto_===Object.prototype);//true
    

    3.如何理解原型?

    答:javascript是基于原型的语言。

    原型得从构造函数说起

      this.name = name;
    }
    fun.prototype.alertName = function(){
      alert(this.name);
    }
    //创建示例
    var f = new fun('zhangsan');
    f.printName = function(){
      console.log(this.name);
    }
    //测试
    f.printName();//zhangsan
    f.alertName();//zhangsan
    

    从上面示例可以知道,当视图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的proto(即它的构造函数的prototype)中寻找,因此f.alertName就会找到fun.prototype.alertName.

    4.如何理解JS的原型链?

    答:以上面的代码块为例,当调用f.alertName()函数时,首先会查找f对象中有无alertName方法,在f对象中没有找到该方法,然后就开始查找f.proto也就是其实例fun.prototype有无alertName,找到之后就引用该方法。

    但是如果用f.toString()调用时,同样会先开始找f对象有无toString方法,然后就去f.proto中查找,如果没找到,那就继续去f.proto.proto_里面查找,这种过程因为是以链式结构去查找,所以称为原型链。如果一直都找不到的话,则宣告失败,浏览器会报错。

    5.作用域分为几种?

    答:分为全局作用域和局部作用域,在es6中添加了块作用域。全局作用域即在最外部定义变量或者用window定义变量来表示。而局部作用域是在函数内用var定义变量;es6中的let和const都是定义块级作用域,不同的是let是可变参数,而const是不可变变量;

    6.什么是闭包?闭包有什么作用?

    答:闭包就是能够读取其它函数内部变量的函数。最简单的用法是

    <pre>function test(){
    var name = "Mozilla";
    function closure(){
    console.log(name);
    }
    closure();
    }
    test();
    </pre>

    在以上代码中test()创建了一个局部变量name和一个名为closure的函数,closure是test函数里面的一个内部函数;当执行test时,同时会执行内部函数closure,就会打印出test作用域中的name的值;还有一个比较经典的例子

    <pre>for(var i = 0;i<6;i++){
    setTimeout(function(){
    console.log(i);//6,6,6,6,6,6
    },0)
    }
    </pre>

    在上面代码中打印出6个6;如果想要输出0,1,2,3,4,5,则需要使用闭包来实现(es6中可通过块作用域实现)

    <pre>for(var i=0;i<6;i++){
    setTimeout(function(i){
    console.log(i)
    }(i),0)
    }
    //或者
    for(var i=0;i<6;i++){
    setTimeout(function(i){
    return function(){
    console.log(i)
    }
    }(i))
    }
    //es6块作用域解决该问题
    for(let i =0;i<6;i++){
    setTimeout(function(){
    console.log(i)
    })
    }
    </pre>

    闭包主要有两个作用场景:

    • 函数作为返回值

    • 函数作为参数传递

    <pre>//函数作为返回值
    function sum(x){
    return function (){
    console.log(x)
    }
    }
    var test1 = new sum(5);
    var x = 55;
    test1();//5

    //函数作为参数传递
    function sum1(x){
    return function(y){
    return x+y
    }
    }
    var test2 = new sum1(5);
    test2(6);//11
    </pre>

    7.如何理解任务队列?

    答:因为javascript是单线程,所以在线程上的任务都得排队,也就是说只有等到前面一个任务执行完成时,才会执行后面的任务。这样的话会造成一个问题,即当前面一个任务执行时间很长的时候,后面的任务就必须得等到前面的任务执行完之后才会执行,会造成空白页的现象。

    为了解决这个问题,javascript设计者就将任务分为2种,一种是同步任务,另外一种是异步任务。同步任务就是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入“任务队列”的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。异步任务包括宏任务微任务

    8.什么是异步任务?异步有哪几种形式?

    答:异步是需要等到主线程任务执行完之后才执行的任务。异步有以下四种形式:

    1.setTimeout/setInterval;

    2.ajax;

    3.dom事件绑定

    4.<img>图片引入

    setTimeout代码示例

    <pre>console.log('第一步');
    setTimeout(function(){
    console.log('第二步');
    },0)
    console.log('第三步');

    //结果输出
    //‘第一步’
    //‘第三步’
    //‘第二步’
    </pre>

    ajax代码操作示例

    <pre>var xhr = new XMLHttpRequest();//创建一个XMLHttpRequest对象实例
    xhr.onreadystatechange = function(){
    //这里的函数是异步执行
    if(xhr.readyState===4){
    if(xhr.status===200){
    console.log(xhr.responseText);
    }
    }
    }
    xhr.open("get","http://www.runoob.com/try/ajax/ajax_info.txt",true);
    xhr.send();
    </pre>

    dom事件绑定示例

    <pre><button id="clickFun">点击我</button>
    <script>
    var clickNode = document.getElementById("clickFun");
    clickNode.addEventListener('click',function(){
    console.log("点击成功");
    })
    </script>
    </pre>

    <img>标签引入示例

    <pre>console.log('start')
    var img = document.createElement('img')
    // 或者 img = new Image()
    img.onload = function () {
    console.log('loaded')
    img.onload = null
    }
    img.src = '/xxx.png'
    console.log('end')
    </pre>

    9.es6的箭头函数相比于函数有什么优点,哪些场景需要用到?

    答:箭头函数和之前的函数相比,节省了一些代码,增加了工作效率;箭头函数主要是用于解决this的指向问题。例如

    <pre>function test(){
    var arr = [1,2,3];
    console.log(this);//这里的this指向{name:'wang'}对象
    //函数表达式
    arr.map(function(){
    console.log(this);//这里的this指向window对象,指向比较混乱
    })
    //箭头函数
    arr.map((item)=>{
    console.log(this)//这里的this指向父元素作用域,即{name:'wang'}对象
    })
    }
    test.call({name:'wang'});
    </pre>

    10.es6中的module有什么作用?

    答:ES6中模块化语法更加简洁,如果只是输出一个唯一对象,可以通过

    export default {}表示

    例如

    index.js

    <pre>export default{
    a:100
    }
    </pre>

    test.js

    <pre>import index from './index.js' //假如index.js和test.js在同一文件夹中
    console.log(index.a);//100
    </pre>

    如果想要输出多个对象,就不需要default了,在引入的时候用{}表示,例如:

    index.js

    <pre>export function fn1(){
    console.log('fn1')
    }
    export function fn2(){
    console.log('fn2')
    }
    </pre>

    test.js

    <pre>import {fn1} from './index.js'
    fn1();//fn1
    </pre>

    11.ES6 class和普通构造函数的区别?

    答:构造函数通常的写法

    <pre>//创建一个构造函数Animal
    function Animal(type){
    this.type = type;
    }
    //构造函数的原型
    Animal.prototype = {
    canEat:function(){
    ....
    }
    }
    //创建一个实例
    var theAnimal = new Animal('dog');
    theAnimal.canEat();
    </pre>

    class的写法

    <pre>class Animal{
    constructor(x.y){
    this.x = x;
    this.y = y;
    }
    add(){
    return this.x +this.y;
    }
    }
    const m = new Animal(1,2);
    console.log(m.add())
    </pre>

    由以上两个代码可知es6中的类是由constructor来创建一个构造函数,然后就直接添加函数,在这里是不存在原型的。

    在继承方面,class的使用无疑会更加简单,例如:

    JS构造函数实现继承:

    <pre>// 动物
    function Animal() {
    this.eat = function () {
    console.log('animal eat')
    }
    }
    // 狗
    function Dog() {
    this.bark = function () {
    console.log('dog bark')
    }
    }
    Dog.prototype = new Animal()
    // 哈士奇
    var hashiqi = new Dog()
    </pre>

    es6 class实现继承

    <pre>class Animal {
    constructor(name) {
    this.name = name
    }
    eat() {
    console.log(${this.name} eat)
    }
    }

    class Dog extends Animal {
    constructor(name) {
    super(name)
    this.name = name
    }
    say() {
    console.log(${this.name} say)
    }
    }
    const dog = new Dog('哈士奇')
    dog.say()
    dog.eat()
    </pre>

    12.ES6中的set的应用场景是什么?

    答:

    set主要应用于以下几个场景:

    1.数组去重;

    <pre>var arr = [1,2,1,1,1,2,3];
    var unique = [...new Set(arr)];
    console.log(unique)//1,2,3
    </pre>

    2.两个数组并集

    3.两个数组之间的交集

    4.两个数组之间的差集

    <pre>var a = new Set([1,2,1,3]);
    var b = new Set([2,3,4,5,2])
    //两个数组并集
    let union = new Set([...a,...b]);//[1,2,3,4,5]
    //交集
    let intersect = new Set([...a].filter(x=>b.has(x)));//2,3
    //差集
    let different = new Set([...a].filter(x=>!b.has(x)));//1
    </pre>

    2.浏览器相关的api

    2.1 使用原生js写出dom的增删查改

    答:

    查看:相关api有getElementsByTagName,getElementById,getElementsByClassName,querySelectorAll等等。

    <pre>// 通过 id 获取
    var div1 = document.getElementById('div1') // 元素

    // 通过 tagname 获取
    var divList = document.getElementsByTagName('div') // 集合
    console.log(divList.length)
    console.log(divList[0])

    // 通过 class 获取
    var containerList = document.getElementsByClassName('container') // 集合

    // 通过 CSS 选择器获取
    var pList = document.querySelectorAll('p') // 集合
    //获取父元素节点
    var div1 = document.getElementById('div1');
    var parent = div.parentElement;
    //获取子元素节点
    var div1 = document.getElementById('div1');
    var child = div.childNodes;
    </pre>

    修改:

    <pre>var div1 = document.getELemenById('div1');
    // 移动已有节点。注意,这里是“移动”,并不是拷贝
    var p2 = document.getElementById('p2')
    div1.appendChild(p2)
    </pre>

    增加:

    <pre>var div1 = document.getELemenById('div1');
    //添加新节点
    var p1 = document.createElement('p');
    p1.innerHTML = '添加DOM节点';
    div1.appendChild(p1);//将p1标签放在id为div1的下方;
    </pre>

    删除:

    <pre>var div1 = document.getElementById('div1');
    var child = div1.childNodes;
    div1.removeChild(child[0]);
    </pre>

    2.2什么是事件绑定、事件冒泡?

    答:普通的事件绑定写法如下:

    <pre>var btn = document.getElmentById('btn1');
    btn.addEventListener('click',function(event){
    console.log('clicked');
    })
    </pre>

    通用的事件绑定

    <pre>function bindEvent(elem, type, fn){
    elem.addEventListener(type,fn)
    }
    var a = document.getElementById('link1')
    bindEvent('a','click',function(e){
    e.preventDefault()//阻止默认行为
    alert('clicked')
    })
    </pre>

    事件冒泡是由最深的节点开始,然后逐步向上传播事件

    <pre><body>
    <div id="div1">
    <p id="p1">激活</p>
    <p id="p2">取消</p>
    <p id="p3">取消</p>
    <p id="p4">取消</p>
    </div>
    <div id="div2">
    <p id="p5">取消</p>
    <p id="p6">取消</p>
    </div>
    </body>
    </pre>

    对于以上HTML代码结构,点击p1时候进入激活状态,点击其它任何<p>都取消激活状态

    <pre>function bindEvent(elem,type,fn){
    elem.addEventListener(type,fn)
    }
    var body = document.body;
    var p = document.getElementsByClassName('p')
    bindEvent(body,'click',function(e){
    //所有p标签的点击事件都会冒泡到body标签上,因此如果不使用stopPropagation的情况下会触发点击事件
    alert("触发body点击事件")
    })
    var p1 = document.getElementById('p1')
    bindEvent(p1, 'click', function (e) {
    e.stopPropagation() // 阻止冒泡
    alert('激活')
    })
    </pre>

    2.3如何使用事件代理?有什么好处?

    答:事件代理其实就是事件委托。就拿收快递这个例子打比方,如果有一天公司里面的三人都会收到快递,平常情况下都是一个一个去拿快递,这样的话就会麻烦很多。但是如果把这些快递都放到前台,由前台小姐姐分发快递,这样的话会大大提高效率。

    在上面的例子中前台小姐姐实际上就起到了代理的作用,话说回来事件代理也是这样,因为事件存在冒泡机制,也就是事件会逐级向上传递信息,利用这个原理我们就可以做事件代理了。例如有这么一个场景:

    <pre><ul id="ul1">
    <li><a class='list'>开始</a></li>
    <li><a class='list'>暂停</a></li>
    <li><a class='list'>取消</a></li>
    </ul>
    </pre>

    如果要获取a中的点击事件,这时候该怎么办?

    一般情况下都会这么写

    <pre><script>
    var aNode = document.getElementsByClassName('list');
    for(var i =0;i<aNode.length;i++){
    aNode[i].addEventListener(function(e){
    console.log(e);
    })
    }
    </script>
    </pre>

    上面的写法会调用多次dom监听a标签,这样的话性能会浪费很多。如果使用事件代理,即通过事件冒泡的形式监听父元素的点击事件,改进之后的写法

    <pre><script>
    var ul1 = document.getElementById("ul1");
    function bindEvent(elem,type,selector,fn){
    if(fn===null){
    var fn = selector;
    selector = null;
    }
    elem.addEventListener(type,function(e){
    var theEvent= e||window.event;//兼容火狐
    var srcElement = theEvent.srcElement;//兼容ie
    if(!srcElement){
    srcElement = theEvent.target;
    }
    //代理存在时
    if(selector){
    if(srcElement.match(selector)){
    fn.call(srcElement,theEvent)
    }
    }else{
    fn(theEvent);
    }
    //无代理
    });
    }
    var list = document.getElementsByClassName('list');
    bindEvent(ul1,'click',list ,function(e){
    console.log(e);
    })
    </script>
    </pre>

    这样写只会监听一个标签事件,会大大减少内存,提高浏览器性能。而且还能使代码变得更加简洁。

    2.4手写XMLHttpRequest不借助任何框架

    答:

    <pre>var xhr = new XMLHttpRequest();//创建XMLHttpRequest对象
    xhr.onreadystatechange = function(){
    if(xhr.readyState===4&&xhr.status===200){
    console.log(xhr.responseText);
    }
    }
    xhr.open('GET',url);
    xhr.send();

    </pre>

    状态码说明:

    从上述代码克制,有两处状态码需要说明;分别是xhr.readyState和xhr.status;

    xhr.readyState的状态码说明:

    0-代理被创建,但尚未调用open()方法。

    1-open()方法已经被调用。

    2-send()方法已经被调用,并且头部和状态已经获得。

    3-下载中,responseText返回部分数据

    4-下载操作已完成

    xhr.status状态码说明

    • 200正常

    • 3xx

    • 301 永久重定向。如http://xxx.com这个 GET 请求(最后没有/),就会被301到http://xxx.com/(最后是/)

    • 302 临时重定向。临时的,不是永久的

    • 304 资源找到但是不符合请求条件,不会返回任何主体。如发送 GET 请求时,head 中有If-Modified-Since: xxx(要求返回更新时间是xxx时间之后的资源),如果此时服务器 端资源未更新,则会返回304,即不符合要求

    • 404 找不到资源

    • 5xx 服务器端出错了

    相关文章

      网友评论

          本文标题:javascript基本知识集合

          本文链接:https://www.haomeiwen.com/subject/dbwrcqtx.html