链接:https://blog.csdn.net/qq_54753561/article/details/122149197?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-122149197-blog-122612776.pc_relevant_aa2&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-122149197-blog-122612776.pc_relevant_aa2&utm_relevant_index=1

1.javascipt的数据类型

1.1基本类型

Number、String、Boolean、Undefined、null、symbol、BigInt

1.2引用类型

  1. Object(包含ObjectArray functionDateRegExp

1.3存储方式的区别

基本数据类型:直接存储在栈内存中,占据空间小,大小固定,属于被频繁使用的数据。指的是保存在栈内存中的简单数据段;number string 布尔

引用数据类型:同时存储在栈内存与堆内存中,占据空间大,大小不固定。

引用数据:类型将指针存在栈中,将值存在堆中。 当我们把对象值赋值给另外一个变量时,复制的是对象的指针,指向同一块内存地址,意思是,变量中保存的实际上只是一个指针,这个指针指向内存堆中实际的值,数组 对象等

堆(heap)和栈(stack)有什么区别和存储机制

: 是一种连续储存的数据结构,具有先进后出后进先出的性质。通常的操作有入栈(压栈),出栈和栈顶元素。想要读取栈中的某个元素,就是将其之间的所有元素出栈才能完成。

: 是一种非连续的树形储存数据结构,具有队列优先,先进先出; 每个节点有一个值,整棵树是经过排序的。特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。常用来实现优先队列,存取随意。

1.4 undefined和null的区别

在 if 语句中 null 和 undefined 都会转为false两者用相等运算符比较也是相等

首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。

不同:

undefined:表示不存在这个值。一个表示”无”的原始值或者说表示”缺少值”,就是此处应该有一个值,但还没有定义。当尝试读取时会返回 undefined

例如变量被全局声明了,但没有赋值时,就等于 undefine

null: 表示一个对象被定义了,值为”空值“

1.5JavaScript什么情况下会返回undefined值?

1、访问已声明,但还未初始化的变量

2、访问不存在的对象属性

3、定义了形参,没有传实参,显示undefined

4、访问任何被设置为undefined值的变量

5、函数没有定义return或return后没有任何东西时

1.6js数据类型判断,条件分支

  1. 所有基本类型中Boolean值是false的只有6个,分别是 : 0 NaN ' ' null undefined false 引用类型Boolean值全是true.
  2. if条件是单个值时,如果是truly值,条件成立, 如果是falsely值,条件不成立

2.javaScript数据类型检测的方法

2.1 typeof

判断简单数据类型

1.检测变量类型的语法:typeof( 变量名 ) 或者 typeof 变量名 typeof后面的括号可以省略。

console.log(typeof 1); //number

console.log(typeof “str”); //string

console.log(typeof true); //boolean

console.log(typeof null); //object 空对象类型,属于对象一类,特殊的

console.log(typeof undefined); //undefined

console.log(typeof [1, 2]); //object

console.log(typeof fn); //function

2.typeof返回的值都是string类型

2.2 toString

toString判断类型的语法:Object.prototype.toString.call(需要判断的变量)

3.Javascript创建对象的几种方式

3.1 字面量方式

3.2使用构造函数⭐⭐

构造函数在js(ES5)中相当于其它面向对象编程语言中的类,对象称为类的实例,类称为对象公共特性的抽象。构造函数创建对象的过程又称为实例化。

new关键字的作用:

1.创建空对象并让this指向该空对象

2.执行构造函数,给空对象增加属性

3.返回已增加了属性的对象

3.3工厂模式

就是将对象中的属性作为参数传入一个函数中,在这么函数内部通过字面量或者构造函数创建一个空对象,将这些传入参数加入到空对象中后,再返回这个对象

3.4使用原型对象的方式prototype关键字

function Dog() {}

Dog.prototype.name = “小黑”;

Dog.prototype.eat = function() {

console.log(this.name)

}

const ruiky = new Dog()

3.5混合模式(原型和构造函数)

此处省略。。。

4.如何区分数组和对象?

1.通过ES6中的Array.isArray 来识别

Array.isArray( [ ] ) // true

Array.isArray( { } ) // false

2.通过instanceof 识别

[ ] instanceof Array // true

{ } instanceof Array // false

3.通过调用constructor来识别

{ }.constructor // Object

[ ].constructor // Array

4.通过 Object.prototype.toString.call

Object.prototype.toString.call(需要判断的变量)

5.JS垃圾回收机制

6.作用域和作用域链

7.this指向的五种情况

8.什么是原型、原型链

什么是原型对象、实例对象

函数有原型,函数有一个属性叫prototype,函数的这个原型指向一个对象,,这个对象就做原型对象。

所有原型对象都会自动获得一个constructor(构造函数),这个构造函数也具有一个指向prototype属性所在函数的指针,即constructor也指向这个函数本身。

实例对象也存在一个指针指向原型对象(proto),proto一般叫做隐身原型,这个隐身原型有一个constructor属性,该属性指向创建该实例的构造函数,之所以使用proto是因为没有一个标准的方式访问实例的prototype,但是Firefox,Safari,Chrome浏览器都支持一个属性proto指向实例对象的原型对象。

在每一个实例对象中的proto中同时有一个constructor属性,该属性指向创建该实例的构造函数。

  1. function Animal() {}
  2. const animal1 = new Animal();
  3. const animal2 = new Animal();
  4. console.log(animal1.constructor === Animal); // true
  5. console.log(animal2.constructor === Animal); // true
  6. console.log(animal1.constructor === animal2.constructor); // true

实例对象proto和构造函数prototype的关系

每一个实例对象中的proto(原型对象)与构造函数中的prototype(原型对象),都指向同一个原型对象,两个是相等的

function Animal() {}

const animal1 = new Animal();
console.log(animal1.__proto__ === Animal1.prototype); // true

构造函数,prototype原型对象,实例对象,proto,constructor之间的关系

原生JS面试题 - 图1

什么是原型链

从上面的介绍我们可以看见,原型对象存在一个指向构造函数的指针,构造函数和构造函数创建的实例对象都有一个指向原型对象的指针,如果我们将一个原型对象指向另一个类创建的实例,结果就是我们的原型创建的实例对象指向原型对象,原型对象存在一个指针指向另一个原型对象,这样一直链式调用,就产生了我们所说的原型链。

常规的原型链层级

最顶层:Object.prototype

第二层:构造函数和实例对象指向的Prototype

第三层:实例对象

原型下的方法:

instanceof:判断一个实例对象是否是某个类下的实例对象。

hasOwnPrototype:判断一个属性是否是实例对象下的属性,不包括原型链上继承的。

in操作符:判断一个属性是否是实例对象下的属性,包括原型链上的继承的。

9.闭包是什么

一、闭包的特点

1.在函数内部嵌套函数;

2.内部函数使用外部函数的参数和变量

3.重点:函数内部的变量(函数参数)不会被浏览器的垃圾回收机制回收,闭包可以使得它的诞生环境一直存在。

闭包使得函数内部环境一直存在,除非非手动销毁,将值设为null;

简单的理解

闭包函数就是一个声明在函数内部的函数;

闭包就是内部函数总是可以访问外部函数的参数和变量,并且在外部函数被返回后仍然可以访问;

综合理解:闭包就是可以创建一个独立的环境(在内存中),每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,每次外部函数执行的时 候,外部函数的引用地址不同(将外部函数赋值给一个外部变量或者叫活动对象),都会重新创建一个新的地址。只要当前活动对象(外部变量)中有被内部子集(内部指函数内部)引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。

例子:这是一段闭包改造后的自执行函数。

1.我们可以看见因为外部类数组 list 的子项被 for 循环内的函数内部子集调用了,每一次迭代都给 list 对应的子集在内存中开辟了一块独立区域存放参数 i 。 2.所以在 for 循环的迭代过程中,每一次迭代的结果 i 都被一个 list 子项在内存中保存下来。 3.在我们没有进行点击事件之前,代码最下方的打印会先打印一个4,因为点击事件相当于是一个异步函数,所以先把 for 循环的最终迭代结果打印出来。 4.在我们点击每个 list 子项时,会将每个 list 子项在内存中所保存的参数打印出来。
var list = document.querySelectorAll('li'); //4个li
for (var i = 0; i < list.length; i++) { //i:0,1,2,3
    (function (i) { //i是参数,相当于函数内部的变量,形成闭包,i一直存在。i:0,1,2,3  
        list[i].onclick = function () {
            console.log(i); // 0,1,2,3
        }
    })(i); //i:0,1,2,3
}
console.log(i); //4,循环的最后一次值。

循环和定时器的问题

for (var i = 1; i <= 5; i++) {
    setTimeout(function() {
        console.log('hehe'); //定时器会被执行5次
    }, 1000);
}
for (var i = 1; i <= 5; i++) {
    setTimeout(function() {
        console.log(i); //定时器会被执行5次  异步:等到循环执行完成。  这里的值是6,因为定时器异步执行,而循环的执行顺序在定时器前,所以优先循环完5次定时器后,等定时器读取 i 的值的时候 i 就已经是6了
    }, 1000);
}

// 改为闭包实现
for (var i = 1; i <= 5; i++) { // i:1,2,3,4,5
    ! function(i) { //i:i是参数,相当于每一轮循环的 i 的值保存进这个自循环函数中了,循环了五次就相当于创建了五个闭包,保存了五个 i 的值
        setTimeout(function() {
            console.log(i); //1,2,3,4,5
        }, 1000);
    }(i); //1,2,3,4,5
}

闭包的优缺点—面试题

1.闭包的好处:

希望一个变量长期存储在内存中(延长变量作用域) - 循环添加事件,里面的循环变量(循环里面使用定时器)

避免全局变量的污染,局部变量(函数内部的变量)的存在。

2.闭包的缺点:

2.1.闭包使得内部的变量一直存在,变量常驻内存,增加内存使用量—手动销毁,变量的值设为null

2.2.使用不当会造成内存泄漏

内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。

当变量标记为离开环境或者变量引用计数为0,而垃圾回收机制无法回收时,就会产生内存泄漏。

经典面试题

var name = "The Window"; // var声明的变量也是window下面的属性。
var object = {
    name:"My Object",
    getNameFunc:function() { //this->object
        return function() {
            return this.name; //this->window,不是object直接调用,里面的方法返回的函数表达式直接使用。
        };
    }
};
alert(object.getNameFunc()); //返回的是getNameFunc内部的函数体
alert(object.getNameFunc()()); //The Window
var name = "The Window";
var object = {
    name:"My Object",
    getNameFunc:function() { //this->object
        var that = this; //将指向object的this赋值给that,that指向object的this
        return function() {
            return that.name;
        };
    }
};
alert(object.getNameFunc()()); //My Object

10.防抖、节流

函数的防抖

定义:当事件被触发一段时间后再执行事件,如果在这阶段时间内事件又被重新触发那么就重新开始计时。

方法,通过函数的闭包和定时器函数组合生效

function debounce(fn, time) { //fn:事件处理函数
    let t = null;
    return () => {
        clearTimeout(t); //将前面开启的定时器关闭,防止定时器叠加。
        t = setTimeout(() => {
            fn.call(this, arguments[0]); //将this指向事件处理函数里面的this
            // fn.apply(this, arguments);
        }, time);
    }
}
const search = document.querySelector('input');
let num = 0;
function thing(e) { //事件处理函数(事件对象,this)
    num++;
    console.log(num);
    console.log(e); //恢复第一次参数是事件对象
    console.log(this); //恢复this指向当前操作的元素
}
search.oninput = debounce(thing, 1000); // debounce(thing)的结果一定是函数体

函数的节流

定义:函数的节流是指在规定的时间内,该事件函数最多只能触发一次,在规定时间内多次触发,不会重新计时;

方法:通过函数闭包和日期构造函数的时间戳方法(当然也可以是别的自动计时函数)来触发。

function throttle(fn, time) { //形成闭包
    let start = 0; //初始时间
    return function() {
        let current = new Date().getTime(); //获取当前时间的时间戳。
        if (current - start >= time) {
            fn.call(this, arguments[0]);
            start = current; //将当前时间给起始时间(将获取的当前时间给初始值,继续获取当前时间)
        }
    }
}
const search = document.querySelector('input');
let num = 0;
function thing(e) { //事件处理函数(事件对象,this)
    num++;
    console.log(num);
    console.log(e); //恢复第一次参数是事件对象
    console.log(this); //恢复this指向当前操作的元素
}
search.oninput = throttle(thing, 1000); //throttle(thing)的结果一定是函数体

11.事件轮询

12.Async、Generate、Promise

13.构造函数

ES5中通过函数创建类,ES6中通过class语法糖创建类,但是在ES6的class方式实际上还是通过原型和构造函数创建类。

同时ES6创建类的方法不像ES5一样,具有变量提升的能力,所以我们如果用class创建一个类,那么只能才class之后才能new这个类的实例。

static

static:静态成员在类定义中使用static关键字作为前缀,在静态成员中,this指向类自身(而非实例对象),与原型成员类似,静态成员每个类上只能有一个。

static声明的静态属性和方法都可以被子类继承。

class Person {
  constructor() {
    // 添加到this的所有内容都会存在于不同的实例上
    this.locate = () => console.log("instance", this);
  }
  // 定义在类的原型对象上
  locate() {
    console.log("prototype", this);
  }
  // 定义在类本身上
  static locate() {
    console.log("class", this);
  }
}

let p = new Person;
p.locate(); // instance Person{locate:[Function(anonymous)]}
Person.prototype.locate(); // prototype {}
Person.locate(); // class [class Person]

继承

ES5实际上是先创建子类的实例对象,再将父类的方法添加到this上(Parent.apply(this)),通过原型和构造函数的机制实现。

ES6的继承实际上是先通过父类的实例对象创建子类的实例对象,然后再将this绑定在子类的实例对象上,然后再用子类的构造函数修饰this。

ES6中通过extends关键字,就可以继承任何拥有【constructor】和原型的对象,这不仅可以继承一个类,也可以继承一个构造函数(向ES5的构造函数兼容)。

ES6中派生类的方法可以通过super关键字引用他们的原型,这个关键字只能在派生类中使用,并且仅限于类的构造函数、实例方法和静态方法的内部。在类的构造函数中使用super可以调用父类的构造函数。

class Parent {
  constructor(a, b) {
    this.a = a;
    this.b = b;
  }
  parentMethods() {
    return this.a + this.b
  }
}
class Child extends Parent {
  constructor(a, b, c) {
    super(a, b); // 通过super调用父类的构造函数构造子类的实例对象
    this.c = c;
  }
  childMethods() {
    return this.c + "," + super.parentMethods() // 通过super实例化调用父类的实例(原型)方法
  }
}
const point = new Child(1, 2, 3);
console.log(point.childMethods());

14.什么是模块化开发

15.js的几种模块规范

js 中现在比较成熟的有四种模块加载方案:

第一种是 CommonJS 方案,

它通过 require 来引入模块,通过 module.exports 定义模块的输出接口。这种模块加载方案是服务器端的解决方案,它是以同步的方式来引入模块的,因为在服务端文件都存储在本地磁盘,所以读取非常快,所以以同步的方式加载没有问题。但如果是在浏览器端,由于模块的加载是使用网络请求,因此使用异步加载的方式更加合适。

第二种是 AMD 方案,

这种方案采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js 实现了 AMD 规范。

第三种是 CMD 方案,

这种方案和 AMD 方案都是为了解决异步模块加载的问题,sea.js 实现了 CMD 规范。它和require.js的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。

第四种方案是 ES6 提出的方案,

使用 import 和 export 的形式来导入导出模块。

16.Promise

17.JS延迟加载的方式

18.柯里化函数和高阶函数

19.

js

  1. 继承的方式
  2. 事件循环
  3. 基本数据类型
  4. 判断数据类型的方式 判断是否是数组 typeof null返回什么
  5. js继承的方式有哪些?怎么实现的
    原型继承
    组合继承
    寄生式继承
    extends关键字

输入url之后发生了什么?

    1. 对输入的域名进行dns解析,再通过解析到的ip访问服务器

    2. 访问服务器开始建立tcp连接

    3. 三次握手 1.客户端发送一个 syn([synchronize](https://cn.bing.com/dict/search?q=synchronize&FORM=BDVSP2&qpvt=Synchronize+)) seq=1(sequence) ack(acknowledgment)=0发送给服务端 2.服务端接收到之后 返回seq=2 ack = seq+1 3.客户端收到之后 再次将ack  = seq + 1返回给服务端

    4. 服务端返回页面数据,以二进制数据流的方式发送回来

    5. 客户端收到数据之后,浏览器引擎开始解析

    6. 创建DOM tree

    7. 创建Style Rules

    8. 构建Render tree

    9. 布局Layout
    10. 绘制Painting
    11. 显示Display