美文网首页
JS常见面试题

JS常见面试题

作者: 吃肉肉不吃肉肉 | 来源:发表于2020-03-15 22:51 被阅读0次

    1.document load和document ready的区别

    1.load是当页面所有资源全部加载完成后(包括DOM文档树,css文件,js文件,图片资源等),执行一个函数
    问题:如果图片资源较多,加载时间较长,onload后等待执行的函数需要等待较长时间,所以一些效果可能受到影响

    2.$(document).ready()是当DOM文档树加载完成后执行一个函数 (不包含图片,css等)所以会比load较快执行
    在原生的jS中不包括ready()这个方法,只有load方法就是onload事件

    2. JavaScript 中如何检测一个变量是一个 String 类型?

    ①typeof
    typeof('123') === "string" // true
    typeof '123' === "string" // true
    
    ②constructor
    '123'.constructor === String // true
    
    ③Object.prototype.toString.call()
    Object.prototype.toString.call('123') === '[object String]' // true
    

    3.请用 js 去除字符串空格?

    replace 正则匹配方法、str.trim()方法、JQ 方法:$.trim(str)

    方法解析:

    方法一:replace 正则匹配方法

    去除字符串内所有的空格:str = str.replace(/\s/g,"");
    去除字符串内两头的空格:str = str.replace(/^\s| \s * (美元符号)/g,"");
    去除字符串内左侧的空格:str = str.replace(/^\s/,"");
    去除字符串内右侧的空格:str = str.replace(/(\s$)/g,"");

    示例:
    var str = " 6 6 ";
    var str_1 = str.replace(/\s*/g, "");
    console.log(str_1); //66
    
    var str = " 6 6 ";
    var str_1 = str.replace(/^\s*|\s*$/g, "");
    console.log(str_1); //6 6//输出左右侧均无空格
    
    var str = " 6 6 ";
    var str_1 = str.replace(/^\s*/, "");
    console.log(str_1); //6 6 //输出右侧有空格左侧无空格
    
    var str = " 6 6 ";
    var str_1 = str.replace(/(\s*$)/g, "");
    console.log(str_1); // 6 6//输出左侧有空格右侧无空格
    

    方法二:str.trim()方法

    trim()方法是用来删除字符串两端的空白字符并返回,trim 方法并不影响原来的字符串本身,它返回的是一个新的字符串。
    缺陷:只能去除字符串两端的空格,不能去除中间的空格

    示例:
    var str = " 6 6 ";
    var str_1 = str.trim();
    console.log(str_1); //6 6//输出左右侧均无空格
    

    方法三:JQ 方法:

    .trim() 函数用于去除字符串两端的空白字符。注意:$.trim()函数会移除字符串开始和末尾处的所有换行符,空格(包括连续的空格)和制表符。如果这些空白字符在字符串中间时,它们将被保留,不会被移除。

    示例:
    var str = " 6 6 ";
    var str_1 = $.trim(str);
    console.log(str_1); //6 6//输出左右侧均无空格
    
    

    4.js 是一门怎样的语言,它有什么特点

    1.脚本语言。

    JavaScript 是一种解释型的脚本语言,C、C++等语言先编译后执行,而 JavaScript 是在程序的运行过程中逐行进行解释。

    2.基于对象。

    JavaScript 是一种基于对象的脚本语言,它不仅可以创建对象,也能使用现有的对象。

    3.简单。

    JavaScript 语言中采用的是弱类型的变量类型,对使用的数据类型未做出严格的要求,是基于 Java 基本语句和控制的脚本语言,其设计简单紧凑。

    4.动态性。

    JavaScript 是一种采用事件驱动的脚本语言,它不需要经过 Web 服务器就可以对用户的输入做出响应。

    5.跨平台性。

    JavaScript 脚本语言不依赖于操作系统,仅需要浏览器的支持。

    5.== 和 === 的不同

    ==是抽象相等运算符,

    ===是严格相等运算符。

    ==运算符是在进行必要的类型转换后,再比较。===运算符不会进行类型转换,所以如果两个值不是相同的类型,会直接返回false。使用==时,可能发生一些特别的事情,
    例如:

    1 == "1"; // true
    1 == [1]; // true
    1 == true; // true
    0 == ""; // true
    0 == "0"; // true
    0 == false; // true
    

    如果你对==和===的概念不是特别了解,建议大多数情况下使用===

    6.怎样添加、移除、移动、复制、创建和查找节点?

    1)创建新节点

    createDocumentFragment() //创建一个 DOM 片段
    createElement() //创建一个具体的元素
    createTextNode() //创建一个文本节点
    

    2)添加、移除、替换、插入

    appendChild() //添加
    removeChild() //移除
    replaceChild() //替换
    insertBefore() //插入
    

    3)查找

    getElementsByTagName() //通过标签名称
    getElementsByName() //通过元素的 Name 属性的值
    getElementById() //通过元素 Id,唯一性
    

    7.事件委托是什么

    利用事件冒泡的原理,让自己的所触发的事件,让他的父元素代替执行!

    解析:

    1、那什么样的事件可以用事件委托,什么样的事件不可以用呢?
    适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
    值得注意的是,mouseover 和 mouseout 虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。
    不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说 focus,blur 之类的,本身就没用冒泡的特性,自然就不用事件委托了。

    2、为什么要用事件委托

    1.提高性能

    ‹ul›
      ‹li›苹果‹/li›
      ‹li›香蕉‹/li›
      ‹li›凤梨‹/li›
    ‹/ul›
    
    // good
    document.querySelector('ul').onclick = (event) =› {
      let target = event.target
      if (target.nodeName === 'LI') {
        console.log(target.innerHTML)
      }
    }
    
    // bad
    document.querySelectorAll('li').forEach((e) =› {
      e.onclick = function() {
        console.log(this.innerHTML)
      }
    })
    

    2.新添加的元素还会有之前的事件。

    3、事件冒泡与事件委托的对比

    事件冒泡:box 内部无论是什么元素,点击后都会触发 box 的点击事件
    事件委托:可以对 box 内部的元素进行筛选

    4、事件委托怎么取索引?

    ‹ul id="ul"›
            ‹li›aaaaaaaa‹/li›
            ‹li›事件委托了 点击当前,如何获取 这个点击的下标‹/li›
            ‹li›cccccccc‹/li›
        ‹/ul›
        ‹script›
            window.onload = function () {
                var oUl = document.getElementById("ul");
                var aLi = oUl.getElementsByTagName("li");
                oUl.onclick = function (ev) {
                    var ev = ev || window.event;
                    var target = ev.target || ev.srcElement;
                    if (target.nodeName.toLowerCase() == "li") {
                        var that = target;
                        var index;
                        for (var i = 0; i ‹ aLi.length; i++)
                            if (aLi[i] === target) index = i;
                        if (index ›= 0) alert('我的下标是第' + index + '个');
                        target.style.background = "red";
                    }
                }
            }
    ‹/script›
    
    拓展:

    键盘事件:keydown/ keypress /keyup
    鼠标事件:mousedown/ mouseup /mousemove /mouseout /mouseover

    8.javascript 对象的几种创建方式

    第一种:Object 构造函数创建

    var Person = new Object();
    Person.name = "Nike";
    Person.age = 29;
    

    这行代码创建了 Object 引用类型的一个新实例,然后把实例保存在变量 Person 中。

    第二种:使用对象字面量表示法

    var Person = {}; //相当于 var Person = new Object();
    var Person = {
        name: 'Nike';
        age: 29;
    }
    

    对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程。也就是说,第一种和第二种方式创建对象的方法其实都是一样的,只是写法上的区别不同在介绍第三种的创建方法之前,我们应该要明白为什么还要用别的方法来创建对象,也就是第一种,第二种方法的缺点所在:它们都是用了同一个接口创建很多对象,会产生大量的重复代码,就是如果你有 100 个对象,那你要输入 100 次很多相同的代码。那我们有什么方法来避免过多的重复代码呢,就是把创建对象的过程封装在函数体内,通过函数的调用直接生成对象。

    第三种:使用工厂模式创建对象

    function createPerson(name, age, job) {
      var o = new Object();
      o.name = name;
      o.age = age;
      o.job = job;
      o.sayName = function() {
        alert(this.name);
      };
      return o;
    }
    var person1 = createPerson("Nike", 29, "teacher");
    var person2 = createPerson("Arvin", 20, "student");
    

    在使用工厂模式创建对象的时候,我们都可以注意到,在 createPerson 函数中,返回的是一个对象。那么我们就无法判断返回的对象究竟是一个什么样的类型。于是就出现了第四种创建对象的模式。

    第四种:使用构造函数创建对象

    function Person(name, age, job) {
      this.name = name;
      this.age = age;
      this.job = job;
      this.sayName = function() {
        alert(this.name);
      };
    }
    var person1 = new Person("Nike", 29, "teacher");
    var person2 = new Person("Arvin", 20, "student");
    
    对比工厂模式,我们可以发现以下区别:

    1.没有显示地创建对象
    2.直接将属性和方法赋给了 this 对象
    3.没有 return 语句
    4.终于可以识别的对象的类型。
    对于检测对象类型,我们应该使用 instanceof 操作符,我们来进行自主检测:

    alert(person1 instanceof Object); //ture
    
    alert(person1 instanceof Person); //ture
    
    alert(person2 instanceof Object); //ture
    
    alert(person2 instanceof Object); //ture
    

    同时我们也应该明白,按照惯例,构造函数始终要应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。那么构造函数确实挺好用的,但是它也有它的缺点:就是每个方法都要在每个实例上重新创建一遍,方法指的就是我们在对象里面定义的函数。如果方法的数量很多,就会占用很多不必要的内存。于是出现了第五种创建对象的方法

    第五种:原型创建对象模式

    function Person() {}
    Person.prototype.name = "Nike";
    Person.prototype.age = 20;
    Person.prototype.jbo = "teacher";
    Person.prototype.sayName = function() {
      alert(this.name);
    };
    var person1 = new Person();
    person1.sayName();
    
    使用原型创建对象的方式,可以让所有对象实例共享它所包含的属性和方法。如果是使用原型创建对象模式,请看下面代码:
    function Person() {}
    Person.prototype.name = "Nike";
    Person.prototype.age = 20;
    Person.prototype.jbo = "teacher";
    Person.prototype.sayName = function() {
      alert(this.name);
    };
    var person1 = new Person();
    var person2 = new Person();
    person1.name = "Greg";
    alert(person1.name); //'Greg' --来自实例
    alert(person2.name); //'Nike' --来自原型
    

    当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性。这时候我们就可以使用构造函数模式与原型模式结合的方式,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性

    第六种:组合使用构造函数模式和原型模式

    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
    }
    Person.prototype = {
        constructor: Person,
        sayName: function() {
            alert(this.name);
        };
    }
    var person1 = new Person('Nike', 20, 'teacher');
    

    9.JavaScript 继承的方式和优缺点

    六种方式

    一、原型链继承

    缺点:

    1.引用类型的属性被所有实例共享
    2.在创建 Child 的实例时,不能向 Parent 传参

    二、借用构造函数(经典继承)

    优点:

    1.避免了引用类型的属性被所有实例共享
    2.可以在 Child 中向 Parent 传参

    缺点:

    方法都在构造函数中定义,每次创建实例都会创建一遍方法。

    三、组合继承

    优点:

    融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。

    四、原型式继承

    缺点:

    包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。

    五、寄生式继承

    缺点:

    跟借用构造函数模式一样,每次创建对象都会创建一遍方法。

    六、寄生组合式继承

    优点:

    1.这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。
    2.与此同时,原型链还能保持不变;
    3.因此,还能够正常使用 instanceof 和 isPrototypeOf。
    开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式

    10.复杂数据类型如何转变为字符串

    首先,会调用 valueOf 方法,如果方法的返回值是一个基本数据类型,就返回这个值,
    如果调用 valueOf 方法之后的返回值仍旧是一个复杂数据类型,就会调用该对象的 toString 方法,
    如果 toString 方法调用之后的返回值是一个基本数据类型,就返回这个值,
    如果 toString 方法调用之后的返回值是一个复杂数据类型,就报一个错误。

    解析:
    var obj = {
      valueOf: function() {
        return 1;
      }
    };
    console.log(obj + ""); //'1'
    
    var obj = {
      valueOf: function() {
        return [1, 2];
      }
    };
    console.log(obj + ""); //'[object Object]';
    
    var obj = {
      valueOf: function() {
        return [1, 2];
      },
      toString: function() {
        return 1;
      }
    };
    console.log(obj + ""); //'1';
    
    var obj = {
      valueOf: function() {
        return [1, 2];
      },
      toString: function() {
        return [1, 2, 3];
      }
    };
    console.log(obj + ""); 
    // 报错 Uncaught TypeError: Cannot convert object to primitive value
    
    拓展:
    var arr = [
    new Object(), 
    new Date(),
     new RegExp(), 
    new String(), 
    new Number(),
     new Boolean(),
     new Function(), 
    new Array(), Math
        ] 
    console.log(arr.length) // 9
    for (var i = 0; i ‹ arr.length; i++) {
        arr[i].valueOf = function() {
            return [1, 2, 3]
        }
        arr[i].toString = function() {
            return 'toString'
        }
        console.log(arr[i] + '')
    }
    

    相关文章

      网友评论

          本文标题:JS常见面试题

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