打卡学习-JavaScript面试题(一)

121 阅读4分钟

菜鸡打卡

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、constructor、Object. prototype. toString. call()

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 = 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(str)方法

.trim()函数用于去除字符串两端的空白字符。注意:. 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. 语句和控制的脚本语言, 其设计简单紧凑。
  5. 动态性。JavaScript 是一种采用事件驱动的脚本语言, 它不需要经过 Web 服务器就可以对用户的输入做出响应。
  6. 跨平台性。JavaScript 脚本语言不依赖于操作系统, 仅需要浏览器的支持。

5.== 和 === 的不同

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

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

== === 的概念不是特别了解,建议大多数情况下使用===全等.

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

1.创建新节点:

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

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

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

3.查找

  • getElementById()
  • getElementsByTagName()
  • getElementsByName()
  • querySelector()
  • querySelectorAll()
  • ...

7.事件委托是什么?

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

  1. 那什么样的事件可以用事件委托,什么样的事件不可以用呢?
  • 适合用事件委托的事件: clickmousedown,mouseup,keydownkeyupkeypress

  • 值得注意的是,mouseovermouseout 虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。

  • 不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说 focusblur 之类的,本身就没用冒泡的特性,自然就不用事件委托了。

  1. 为什么要事件委托?
  • 提高性能
<ul>
    <li>苹果</li>
    <li>香蕉</li>
    <li>凤梨</li>
</ul>

<script>
    //good
   document.querySelector('ul').onclick = e => {
       let target = e.target
       if (target.nodeName === 'li') {
           console.log(target.innerHTML)
       }
   }
   
   //bad
    document.querySelectorAll('li').forEach(e => {
        e.onclick = function () {
            console.log(this.innerHTML)
        }
    })
</script>

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

  1. 事件冒泡与事件委托的对比
  • 事件冒泡:box 内部无论是什么元素,点击后都会触发 box 的点击事件事件委托:可以对 box 内部的元素进行筛选
  1. 事件委托怎么取索引?
<ul id="ul">
    <li> aaaaaaaa </li>
    <li> 事件委托了 点击当前, 如何获取 这个点击的下标 </li>
    <li> cccccccc </li>
</ul>

<script>
    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. require 与 import 的区别(了解)

两者的加载方式不同、规范不同。

1.两者的加载方式不同,require 是在运行时加载,而 import 是在编译时加载

require('./a')(); // a 模块是一个函数,立即执行 a 模块函数

var data = require('./a'). data; 
// a 模块导出的是一个对象

var a = require('./a')[0]; 
// a 模块导出的是一个数组 ======> 哪都行 
import $ from 'jquery'; import * as _ from'_';

import {a, b, c} from './a';

import {default as alias, a as a_a, b, c} from './a';
//======>用在开头

3.规范不同,require 是 CommonJS/AMD 规范,import 是 ESMAScript6+规范

3.require 特点:社区方案,提供了服务器/浏览器的模块加载方案。非语言层面的标准。只能在运行时确定模块的依赖关系及输入/输出的变量,无法进行静态优化。import 特点:语言规格层面支持模块功能。支持编译时静态分析,便于 JS 引入宏和类型检验。动态绑定。

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

1.Object构造函数创建

var Person = new Object();
Person.name = 'mint';
Person.age = 18;

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

2.使用对象字母面量表示法

var Person = {}; //相当于 var Person = new Object();
var Person = {
    name = 'mint',
    age = 18;
}

对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程。也就是说,第一种和第二种方式创建对象的方法其实都是一样的,只是写法上的区别不同

在介绍第三种的创建方法之前,应该要明白为什么还要用别的方法来创建对象,也就是第一种,第二种方法的缺点所在:它们都是用了同一个接口创建很多对象,会产生大量的重复代码,就是如果你有 100 个对象,那你要输入 100 次很多相同的代码。那有什么方法来避免过多的重复代码呢,就是把创建对象的过程封装在函数体内,通过函数的调用直接生成对象。

3.使用工厂模式创建对象

function createPerson(name, age, job) {
    let o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
        alert(this.name)
    }
    return o;
}

const p1 = createPerson('mint', 28, 'teacher');
const p2 = createPerson('pika', 19, 'student');

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

4.使用构造函数创建对象

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        alert(this.name);
    };
}

const person1 = new Person("mint", 28, "teacher");
const person2 = new Person("pika", 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

同时也应该明白,按照惯例,构造函数始终要应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。

那么构造函数确实挺好用的,但是它也有它的缺点:就是每个方法都要在每个实例上重新创建一遍,方法指的 就是在对象里面定义的函数。如果方法的数量很多,就会占用很多不必要的内存。于是出现了第五种创建对象的方法

5:原型创建对象模式

function Person() {}
    Person.prototype.name = 'mint';
    Person.prototype.age = 18;
    Person.prototype.job = 'student';
    Person.prototype.sayName = function () {
        alert(this.name)
    }
    const p1 = new Person();
    const p2 = new Person();
    p1.name = 'pika'
    alert(p1.name) // 'pika' - 来着实例
    alert(p2.name) // 'mint' - 来着原型

当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性。

这时候就可以使用构造函数模式与原型模式结合的方式,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性.

6.组合使用构造函数模式和原型模式

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

10.什么是原型链?

通过一个对象的 __proto__ 可以找到它的原型对象,原型对象也是一个对象,就可以通过原型对象的 __proto__, 后找到了 Object. prototype, 从实例的原型对象开始一直到 Object. prototype 就是原型链。

image.png