JavaScript面试题

**基础:

对象Object原型上的所有方法?

    • constructor:指向创建对象的函数的引用。
    • hasOwnProperty:检查对象是否具有指定的属性作为自身(非继承)属性。
    • isPrototypeOf(obj):检查该对象是否在指定对象的原型链中。
    • propertyIsEnumerable(propName):检查指定属性是否可枚举。
    • toLocalString(obj):将对象转换为本地字符串。
    • toString(obj):将对象转换为字符串。
    • valueOf(obj):返回对象的原始值。
    • keys:返回一个由对象的所有可枚举属性组成的数组。
    • getPrototypeOf(obj):返回指定对象的原型(内部[[Prototype]]属性的值)。
    • values(obj):返回一个新的数组迭代器,它包含对象自身的所有可枚举属性值。
    • is(val1,val2):判断两个值是否相等,返回true/false;
    • assign(targetObj,sourceObj,s2,s3...)目标对象,源对象 将源对象合并到目标对象,返回修改后的目标对象,浅拷贝。
    • setPrototypeOf(obj,obj2):设置对象的原型;将obj2设置为obj的原型。
    • create(sourceObj): 以源对象为原型创建一个对象并返回.
    • defineProperties(obj,props):要修改的对象,修改后的属性;为一个对象声明一个属性,返回修改后的原对象
    • isExtensible:判断对象是否可扩展。
    • entries:返回一个新的数组迭代器,它包含[key, value]对。
    • freeze:冻结对象,防止添加新属性,删除现有属性或修改现有属性的可枚举性、配置性、可写性和值。
    • getOwnPropertyDescriptor:获取对象的属性描述符。
    • getOwnPropertyNames:返回一个由对象的所有自身可枚举属性名称组成的数组。
    • seal:封闭对象,防止添加新属性,删除现有属性以及修改现有属性的特性和值。
函数:

函数只有当每次调用时才会执行

命名规则:

1.不能以数字开头,但可以包含数字;

2.以_、字母、$开头;

3.小驼峰命名法(myFunctionTest)

创建:

字面量创建:function test(参数){ 函数执行语句};

表达式创建:var test = function(参数){函数执行语句};

参数:形参和实参数量可以不相等

形参:定义函数时在小括号里的参数

实参:调用函数时传入的参数

预编译:

规则:

1.检查通篇的语法错误

2.预编译过程

3.解释一行,执行一行

函数提升:

函数声明整体提升,函数表达式也不会提升;

变量只有声明提升,赋值不提升;

暗示全局变量:imply global variable

a = 1;当变量未被声明就赋值,属于window 全局变量

var a= 1如果var 声明了也是属于全局变量;

函数执行顺序:

函数内部AO(activation object):活跃函数 ->执行上下文

1.先查找形参和变量声明,

2.实参值赋值给形参

3.查找函数声明,赋值

4.执行

函数外部GO(global object):全局上下文.

1.寻找变量声明;

2.寻找函数声明;

3.执行;

作用域:

在程序中定义的变量和方法可以访问的区域或范围;

闭包:

在一个函数内部有权访问到外部函数的变量和方法;

就算外部函数执行完毕,依旧不会被垃圾回收机制回收,因为内部或许正在使用外部方法的引用;

function work(){
    var name = ""
    // var opration = {
    //     who:function(names){
    //         name = names
    //     },
    //     when:function(){
    //         console.log(name + '今天学习')
    //     }
    // };
    // return opration;
    function who(names){
        name = names
    };
    function when(){
        console.log(name + " xiawu xiaxi");
    };
    return [who,when];
}
var work = work()
work[0]('zxy')
work[1]()

立即执行函数:

当程序执行时函数不需要被调用就立即执行,函数执行完毕之后立即回收掉;

只有表达式形式的才可以被()立即执行

否则不可以;

如果想要拿到返回值,需要使用变量将其返回值保存起来

// 表达式:
var name = function (){}()
(function (){}())

//1、 w3c推荐
  var num = (function (a,b){
  var a = 1,
      b = 2;
  console.log(a + b);
  // return a + b;
}(1,2))
// 2、
(function (){})()

// 函数声明 function前 加上 + - ! || && 都可以转换为表达式
+ function (){}()

数组去重方式:

1、 Set方法

console.log([...new Set(arr)])

2、双重for循环:

         let _arr = [1,2,3,4,5]
         for(var i = 0;i < arr.length;i++){
            for(var j = i+1;j < arr.length;j++){
                if(arr[i] == arr[j]){
                    arr.splice(j,1)
                    j--
                }
            }
         }
         console.log(arr)
3、使用filter结合indexOf进行数组去重
            	console.log(arr.filter((item,index)=>arr.indexOf(item)===index))


#1、 call、bind、apply的区别以及作用?##

作用:

call、apply、bind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向

区别:
apply:

立即执行函数,接收两个参数(this的指向(当第一个参数为null、undefined的时候,默认指向window(在浏览器中)),以数组传递的参数);

call:

立即执行函数,接收两个参数,第一个参数this指向,第二个值为参数列表的形式接收,不被[]包裹

bind:

不会立即执行,而是生成一个新的函数,调用函数时触发。

手写call:
Function.prototype.Mycall = function(ctx,...args){
  //使用globalThis来自适应 浏览器环境(window)和服务器环境(global)
  //将ctx进行归一化,为null或undefined时值为全局指向,否则就是使用object包裹而成的对象
  ctx = ctx === null || ctx === undefined ? globalThis: Object(ctx);
  console.log(ctx,args)
  // 定义一个内部函数来执行触发并绑定当前this
  const fn = this;
  // 使用Symbol来定义唯一变量,以免命名冲突
  const key = Symbol('temp')
  Object.defineProperty(ctx,key,{
    value:fn,
    enumerable:false //设置不可枚举
  })
  // 函数返回值
  const res = ctx[key](...args)
  delete ctx[key]
  return res
}
function Runfun(a,b){
  return a+b
}

Runfun.Mycall(this,1,2)
console.log(Runfun(1,2))
手写bind:
function fn(...args) {
  console.log(this,"---",args)
  return 123
}
Function.prototype.MyBind = function (ctx){
  ctx = ctx === null || ctx === undefined ? globalThis:Object(ctx);
  // 获取this后的剩余参数
  const args = Array.prototype.slice.call(arguments,1);
  const fn = this;
  return function A(){
    const restargs = Array.prototype.slice.call(arguments)
    const allArgs = args.concat(restargs)
    // 判断原型是否相同
    if(Object.getPrototypeOf(this) === A.prototype ){
      return new fn(...allArgs)
    }else{
      return fn.apply(ctx,allArgs);
    }
  }
}
var newFn = fn.MyBind(this,1,2,4)
const  res = newFn(1,2)
console.log(res)

#2、继承?##

继承就是一个函数可以拥有另一个函数的方法和属性、节省重复书写代码。

原型链继承、构造函数继承、组合式继承、寄生组合式继承。

原型链继承:

当访问一个对象.属性时,如果找的到则立即返回,找不到则顺着原型对象__proto__属性进行查找,找到则立即返回,没找到则返回null。

缺点:如果继承的对象是引用类型,那么就会被所有实例共享,一个变了其他的都会被改变

// 定义父类
function Parent() {
  this.arr = [1, 2, 3];
}
// 定义子类
function Child() {}

// 子类继承父类,这里是关键,实现原型链继承
Child.prototype = new Parent();

// 实例化子类
var child1 = new Child();
var child2 = new Child();

child1.arr.push(4);

console.log(child1.arr); 
console.log(child2.arr);
构造函数继承:

通过使用call或apply方法在子类中执行父类的构造函数,从而实现继承;

缺点:不能继承父类prototype上的属性;

// 父类
function Parent() {
  this.sayHello = function () {
    console.log("Hello");
  };
}
Parent.prototype.a = "我是父类prototype上的属性";
// 子类
function Child() {
  Parent.call(this);
}

var child1 = new Child();
var child2 = new Child();
var parentObj = new Parent();

console.log(parentObj.a);
console.log(child1.a);
组合式继承:

构造函数继承和原型链继承组合而成的,原型属性不会被共享,可以继承父类原型上的属性和方法。

缺点:调用了两次父类方法;他在子类的prototype上添加了父类的属性和方法;

// 父类
function Parent() {
  this.sayHello = function () {
    console.log("Hello");
  };
}
Parent.prototype.a = "我是父类prototype上的属性"; 

// 子类
function Child() {
  Parent.call(this);
}

Child.prototype = new Parent();

var child1 = new Child();
寄生组合式继承:

使用Object.create()继承了父类原型链的所有方法,原型属性不会被共享,可以继承父类的属性和方法,只调用了一个父类方法,所以不会给子类原型上添加父类的所有方法。

缺点:子类原型上的原始属性和方法会丢失。

// 父类
function Parent() {
  this.sayHello = function () {
    console.log("Hello");
  };
}
Parent.prototype.a = "我是父类prototype上的属性";
// 子类
function Child() {
  Parent.call(this);
}
// 创建一个没有实例方法的父类实例作为子类的原型
Child.prototype = Object.create(Parent.prototype);
// 修复构造函数的指向
Child.prototype.constructor = Child;

var child1 = new Child();

##3、事件模型、事件代理?##

事件和事件流:

事件可以理解为html文档或者浏览器中发生的一种交互操作,是的网页具备互动性;

事件流:包括事件捕获阶段、处于目标阶段、事件冒泡阶段

事件冒泡:

是一种从下往上的传播方式,由最具体的元素然后逐渐向上传播到最顶层;

事件模型:

分为原始事件模型、标准事件模型、IE事件模型

原始事件模型:

事件绑定监听函数比较简单分别是html代码中直接绑定、通过js代码绑定(获取dom元素,添加事件)。

特性:绑定速度快;但是只支持冒泡,不支持捕获,而且同一类型的事件只能绑定一次;

标准事件模型:

一共分为三个过程:事件捕获、事件处理、事件冒泡;

事件捕获:事件从document一直向下传播到目标元素, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行

事件处理:事件到达目标元素, 触发目标元素的监听函数;

事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行

//事件绑定监听函数
addEventListener(eventType, handler, useCapture)

//事件移除监听函数
removeEventListener(eventType, handler, useCapture)

//eventType指定事件类型(不要加on)
//handler是事件处理函数
//useCapture是一个boolean用于指定是否在捕获阶段进行处理,一般设置为false与IE浏览器保持一致
//举例
var btn = document.getElementById('.btn');
btn.addEventListener(‘click’, showMessage, false);
btn.removeEventListener(‘click’, showMessage, false);

特性:可以在一个dom元素上绑定多个相同的事件处理器

IE事件模型:

分为事件处理和事件冒泡阶段;

var btn = document.getElementById('.btn');
btn.attachEvent(‘onclick’, showMessage);
btn.detachEvent(‘onclick’, showMessage);
//这种方法基本不使用;
事件代理:

就是事件委托,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,而不是目标元素;

当事件响应到目标元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数

###this对象的理解?###

定义:

this 关键字是函数运行时自动生成的一个内部对象,只能在函数内部使用,总指向调用它的对象

this指向:

普通函数this指向:取决于执行上下文;箭头函数this指向取决于定义时所处的环境;

默认指向window

使用new 实例一个新对象:指向当前对象

使用对象调用(obj.sun()):指向前面的对象

call、apply、bindt:this指向他们的第一个参数

事件处理函数:指向当前调用它的dom节点;

箭头函数:取决于定义时所处的环境;

1、说说JavaScript中的数据类型?存储上的差别?

基本类型:Number、String、Boolean、undefined、null、symbol

引用类型:对象(object、array、date、math、regExp、function)

存储:基本类型(栈)、引用类型(堆)

栈中存放基本类型的值和引用类型的引用地址、而堆中存放的是引用类型的值;

2、数组的常用方法有哪些?

增:push、unshift、concat

删:pop、shift、splice、slice

改:splice

查:indexOf、includes、find、findIndex

排序:reverse反转、sort排序

迭代:some、every、forEach、filter、map

map/some/every/forEach/reduce

3、JavaScript字符串的常用方法有哪些?

增:concat

删:slice、substr、substring

改:trim、repeat、toLowerCase、toUpperCase

查:chatAt、indexOf、includes

转换:split

模版匹配:match、search、replace

4、数据类型检测?

typeof:检测基本类型、对象类型除了函数都返回object

instanceof:检测引用数据类型、不能检测基本类型

object.prototype.toString.call():任何类型都可以检测

Array.isArray:用于判断是否是数组类型;

// 我们也可以试着实现一下 instanceof
function _instanceof(left, right) {
  // 由于instance要检测的是某对象,需要有一个前置判断条件
  //基本数据类型直接返回false
  if(typeof left !== 'object' || left === null) return false;

  // 获得类型的原型
  let prototype = right.prototype
  // 获得对象的原型
  left = left.__proto__
  // 判断对象的类型是否等于类型的原型
  while (true) {
    if (left === null)
      return false
    if (prototype === left)
      return true
    left = left.__proto__
  }
}

console.log('test', _instanceof(null, Array)) // false
console.log('test', _instanceof([], Array)) // true
console.log('test', _instanceof('', Array)) // false
console.log('test', _instanceof({}, Object)) // true

5、深拷贝浅拷贝的区别?如何实现一个深拷贝?

深拷贝:递归的遍历所有对象属性,复制的是整个对象,开辟了一个新的内存空间,对新数据更改,不会影响原始数据

浅拷贝:复制的是对象的引用地址,跟源对象共用一个内存地址、更改数据会连同源对象一起改变

浅拷贝:Object.assign、Array.prototype.slice()、Array.prototype.concat()、扩展运算符(...)

深拷贝:JSON.parse(JSON.stringify())、递归遍历、第三方库lodash(cloneDeep)

浅拷贝实现:
Object.assign
var obj = {
  age: 18,
  nature: ['smart', 'good'],
  names: {
    name1: 'fx',
    name2: 'xka'
  },
  love: function () {
    console.log('fx is a great girl')
  }
}
var newObj = Object.assign({}, fxObj);
slice:
onst fxArr = ["One", "Two", "Three"]
const fxArrs = fxArr.slice(0)
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
concat:
const fxArr = ["One", "Two", "Three"]
const fxArrs = fxArr.concat()
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
扩展运算符:
const fxArr = ["One", "Two", "Three"]
const fxArrs = [...fxArr]
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
深拷贝实现:
第三方库lodash:
const _ = require('lodash');
const obj1 = {
  a: 1,
  b: { f: { g: 1 } },
  c: [1, 2, 3]
};
const obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
JSON.parse(JSON.stringify())

const obj2=JSON.parse(JSON.stringify(obj1));

递归遍历:
function deepClone(obj){
  // 如果obj为空或没有值时直接跳出循环
  if(typeof obj !== "object" || obj == null){
    return obj
  }
  var result;
  if(obj instanceof Array){
    result = [];

  }else{
    result = {};
  }
  for (let i in obj){
    if(obj.hasOwnProperty(i)){
      result[i] = deepClone(obj[i])
    }
  }
  return result
}
let obj2 = deepClone(obj)

6、说说你对闭包的理解?闭包使用场景

定义->形成->使用场景->弊端->内存泄漏->垃圾回收机制;

定义:闭包就是在一个函数内部有权访问外部函数的变量和属性

形成:

在一个函数内定义另一个函数

在一个函数内返回另一个函数

作用:创建私有属性、延长变量的生命周期

弊端:会占用内存,可能导致内存泄漏;

7、说说你对作用域、作用域链的理解?

定义:

作用域:

在程序中定义的变量和函数,这些变量可被访问的区域或范围;

分为:全局作用域、函数作用域

全局作用域:不在函数内部和大括号内定义的变量和属性都称为全局作用域

函数作用域:又称为局部作用域,在函数内定义的变量必须以函数的形式访问,不能在函数外部访问

// 函数作用域:
function greet() {
  var greeting = 'Hello World!';
  console.log(greeting);
}
// 打印 'Hello World!'
greet();
// 报错: Uncaught ReferenceError: greeting is not defined
console.log(greeting);
作用域链:

定义:作用域链的构建方式是由作用域嵌套关系所决定的;

作用: 当在Javascript中使用一个变量的时候,首先Javascript引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域为止

8、JavaScript原型,原型链 ? 有什么特点?

原型:

定义:原型(Prototype)是 JavaScript 中每个对象都拥有的一个特殊属性。每个对象都有一个指向另一个对象的链接,这个链接就称为原型。

分为显示原型和隐式原型

显示原型:

显示原型是通过函数的 prototype 属性来定义的,它是一个指向一个对象的引用;

显示原型主要用于实现对象之间的共享属性和方法;

隐式原型:

隐式原型是每个 JavaScript 对象都具有的一个特殊属性 __proto__,它指向了该对象的原型;

隐式原型是对象实例与其构造函数之间的链接;

好处:

使用原型的主要好处是可以实现属性和方法的共享,节省内存空间,并且能够动态地为对象添加新的属性和方法

原型链:

原型链是实现继承和属性查找的机制之一,在javascript中,每个对象都有一个指向其原型对象的内部链接,这个原型对象也有自己的原型,依次类推,形成了链接结构,就叫原型链;

当通过一个 对象.属性(obj.name) 时如果自身存在该属性 则返回该属性的值 如果自身没有则顺着 __proto__(隐式原型属性[[Prototype]])进行查找 直到原型链的最顶端(Object.prototype)找到则立即返回 否则返回 null

9、说说你对事件循环的理解?

由于JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环。

任务分为:同步任务、异步任务

同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行;

异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等

执行顺序:

首先在任务队列中查找是否有同步任务,如果有就依次执行;

执行完毕之后,再执行异步任务,先查找是否有微任务,依次执行微任务;

最后执行宏任务,依次执行。重复以上循环,直到所有的任务执行完毕结束循环。

10、说说 JavaScript 中内存泄漏的几种情况?

定义:内存泄漏指的是程序在运行过程中,由于错误的内存管理或者设计缺陷导致的内存无法被正确释放的情况。当程序分配了一块内存空间用于存储数据或对象时,如果在不再需要这些数据或对象时没有正确释放这块内存,就会发生内存泄漏

意外的全局变量、定时器未清除、没有清理对DOM元素的引用、包括使用事件监听addEventListener监听的时候,在不监听的情况下使用removeEventListener取消对事件监听

全局变量:使用var声明的全局变量,垃圾回收机制不确定这个变量什么时候该清理;

定时器:在内部调用了外部的变量,即使函数执行完毕,依旧会调用外部的引用;要及时删除

dom元素:使用完毕之后需要给引用的dom节点设置为null

addEventListener/removeEventListener:使用事件监听之后要移除事件

11、垃圾回收机制?

原理:垃圾回收机制是一种自动管理内存的机制,用于检测并回收不再被程序使用的内存资源,防止内存泄漏和提高程序的性能。

实现:
引用计数:

JavaScript中还使用引用计数来帮助识别不再使用的对象。每个对象都有一个引用计数,当有引用指向该对象时,引用计数加一;当引用被删除或者指向其他对象时,引用计数减一。当引用计数为零时,表示对象不再被引用,可以被垃圾回收, 在循环引用中,计数永远不能0;

标记清除-算法:

标记阶段: 垃圾回收器会标记所有能够访问到的对象,标记为“活动”状态

清除阶段: 垃圾回收器会清除所有未标记的对象,即被标记为“非活动”状态的对象,释放它们占用的内存空间

但由于javascript是单线程的,所以删除的元素就会空出来,所有增加了一个策略first-fit算法---找到能放置下新对象的第一个位置

v8对垃圾回收进行了优化:

多线程并行回收,增量标记(分段执行、交叉执行),三色标记法(将上次标记为灰色)、并发回收(js在主线程执行,垃圾回收在辅助线程)

const arr = [1, 2, 3, 4];
arr = null;

通过设置arr为null,就解除了对数组[1,2,3,4]的引用,引用次数变为 0,就被垃圾回收了

总结:

有了垃圾回收机制,不代表不用关注内存泄露。那些很占空间的值,一旦不再用到,需要检查是否还存在对它们的引用。如果是的话,就必须手动解除引用

12、什么是防抖和节流?有什么区别?如何实现?

定义:

节流:n秒内只运行一次,若在n秒内重复触发,只有一次生效

防抖:n秒后再执行该事件,若在n秒内呗重复触发,则重新计时;

区别:

相同点:都可以通过setTimeout实现;目的相同,都是为了降低回调执行频率,节省计算资源;

不同点:函数防抖(用于合并操作,在规定的时间内再次被触发,重新计时)、函数节流(用于限制触发频率,在规定的时间内只触发一次)

节流实现:
function throttled2(fn, delay = 500) {
  let timer = null
  return function (...args) {
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, args)
        timer = null
      }, delay);
    }
  }
}
防抖实现:
function debounce(func, wait) {
  let timeout;

  return function () {
    let context = this; // 保存this指向
    let args = arguments; // 拿到event对象

    clearTimeout(timeout)
    timeout = setTimeout(function(){
      func.apply(context, args)
    }, wait);
  }
}

13、说说 JavaScript 中存储数据的方法?

cookie、sessionStorage、localStorage

cookie:

存储类型为[小型文本文件],指某些网站为了辨别用户身份而储存在用户本地终端上的数据。是为了解决 HTTP无状态导致的问题

sessionStorage:

会话存储:退出界面数据消息

其他同下;

localStorage:

持久化存储,除非手动删除;在同域名下存储内容共享;受同源策略限制;

存储:

localStorage.setItem('username','cfangxu');

获取:

localStorage.getItem('username')

删除/清空:

localStorage.removeItem('username') //删除指定名称
localStorage.clear() //清除所有

缺点:

不能设置过期时间;

只能存入字符串,无法直接存对象

区别:

存储大小:cookie数据大小不能超过4k;sessionStorage和localStorage可以达到5M或更大

有效时间:cookie可以设置过期时间,即使浏览器关闭下次依然有效; localStorage存储持久数据,除非手动删除否则数据不会丢失;sessionStorage数据在当前浏览器窗口关闭后自动删除;

·数据交互:cookie的数据会自动传递给服务器,服务器也可以写cookie到客户端;sessionStorage和localStorage不会自动将数据发给服务器,只是本地存储。

14、BOM的理解、常见的BOM对象有哪些?

定义:

BOM(Browser Object Model):浏览器对象模型,提供了独立于内容和浏览器窗口进行交互的对象;

作用:

可以跟浏览器进行交互,页面前进、后退、刷新;浏览器窗口变化、滚动条变化;以及浏览器详细信息;

常见的BOM对象:
window:

open():通过特定的url跳转到相应页面;

12 close():关闭当前窗口

location:

属性名

例子

说明

hash

"#contents"

utl中#后面的字符,没有则返回空串

host

www.wrox.com:80

服务器名称和端口号

hostname

www.wrox.com

域名,不带端口号

href

http://www.wrox.com:80/WileyCDA/?q=javascript#contents

完整url

pathname

"/WileyCDA/"

服务器下面的文件路径

port

80

url的端口号,没有则为空

protocol

http:

使用的协议

search

?q=javascript

url的查询字符串,通常为?后面的内容

navigator:

navigator 对象主要用来获取浏览器的属性,区分浏览器类型。属性较多,且兼容性比较复杂

screen:

保存的纯粹是客户端能力信息,也就是浏览器窗口外面的客户端显示器的信息,比如像素宽度和像素高度

history:

history对象主要用来操作浏览器URL的历史记录,可以通过参数向前,向后,或者向指定URL跳转

参数:

history.go():接收一个整数数字或者字符串参数:向最近的一个记录中包含指定字符串的页面跳转;

history.go(3) //向前跳转三个记录
history.go(-1) //向后跳转一个记录

history.back():向后跳转一个页面;

history.forward():向前跳转一个页面

history.length():获取历史记录数

15、DOM的理解?常见的操作?

定义:

文档对象模型(document object model)是html和xml文档的编程接口

任何文档都可以用dom表示为一个有节点构成的层级结构;

常见操作:
创建节点:

createElement():创建新元素,接受一个参数,即要创建元素的标签名

const divEl = document.createElement("div");

创建文件节点:createTextNode()

创建文档碎片:createDocumentFragment() 用来创建一个文档碎片,它表示一种轻量级的文档,主要是用来存储临时节点,然后把文档碎片的内容一次性添加到DOM中

获取节点:
document.getElementById('id属性值');返回拥有指定id的对象的引用
document.getElementsByClassName('class属性值');返回拥有指定class的对象集合
document.getElementsByTagName('标签名');返回拥有指定标签名的对象集合
document.getElementsByName('name属性值'); 返回拥有指定名称的对象结合
document/element.querySelector('CSS选择器');  仅返回第一个匹配的元素
document/element.querySelectorAll('CSS选择器');   返回所有匹配的元素
document.documentElement;  获取页面中的HTML标签
document.body; 获取页面中的BODY标签
document.all[''];  获取页面中的所有元素节点的对象集合型
更新节点:

innerHTML:

// 获取<p id="p">...</p >
var p = document.getElementById('p');
// 设置文本为abc:
p.innerHTML = 'ABC'; // <p id="p">ABC</p >
// 设置HTML:
p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ';
// <p>...</p >的内部结构已修改
添加节点:

appendChild()

添加一个p元素

const js = document.getElementById('js')
js.innerHTML = "JavaScript"
const list = document.getElementById('list');
list.appendChild(js);

insertBefore():

parentElement.insertBefore(newElement, referenceElement)

删除节点:

删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的removeChild把自己删掉

// 拿到待删除节点:
const self = document.getElementById('to-be-removed');
// 拿到父节点:
const parent = self.parentElement;
// 删除:
const removed = parent.removeChild(self);
removed === self; // true

DOM(文档对象模型)中,你可以使用JavaScript来判断节点的类型。以下是一些常见的方法:

  1. 通过nodeType属性:
    • 使用节点的
// 创建一个元素节点
const elementNode = document.createElement('div');
console.log(elementNode.nodeType); // 输出 1,表示元素节点

// 创建一个文本节点
const textNode = document.createTextNode('Hello World');
console.log(textNode.nodeType); // 输出 3,表示文本节点

属性,该属性返回一个表示节点类型的数字。例如:

      • 元素节点的nodeType为 1。
      • 属性节点的nodeType为 2。
      • 文本节点的nodeType为 3。
  1. 通过nodeNamenodeValue属性:
    • 使用节点的
// 创建一个元素节点
const elementNode = document.createElement('div');
console.log(elementNode.nodeName); // 输出 DIV,表示元素的标签名
console.log(elementNode.nodeValue); // 输出 null,元素节点的 nodeValue 为 null

// 创建一个文本节点
const textNode = document.createTextNode('Hello World');
console.log(textNode.nodeName); // 输出 #text,表示文本节点
console.log(textNode.nodeValue); // 输出 Hello World,文本节点的 nodeValue 为文本内容

属性来获取节点的名称和值。例如:

      • 元素节点的nodeName为元素的标签名,nodeValue为 null。
      • 文本节点的nodeName为 #text,nodeValue为文本内容。
  1. 使用instanceof运算符:
    • 使用
// 创建一个元素节点
const elementNode = document.createElement('div');
console.log(elementNode instanceof Element); // 输出 true,表示是元素节点
console.log(elementNode instanceof Text); // 输出 false,不是文本节点

// 创建一个文本节点
const textNode = document.createTextNode('Hello World');
console.log(textNode instanceof Element); // 输出 false,不是元素节点
console.log(textNode instanceof Text); // 输出 true,表示是文本节点

运算符检查节点对象是否属于特定的节点类型。例如:

      • node instanceof Element用于检查是否为元素节点。
      • node instanceof Text用于检查是否为文本节点。

这些方法可以根据你的需求选择合适的方式来判断节点的类型。

16、说说new操作符具体干了什么?

1、创建一个空的对象obj

2、将空对象的原型,指向了构造函数的原型

3、将空对象作为构造函数的上下文(改变this指向)

4、对构造函数有返回值的处理判断(如果构造函数返回的是基本类型则忽略返回值,如果是引用类型则返回引用类型的值);

17、对象创建方式?

1、工厂模式:

步骤:创建工厂函数==>创建对象实例==>返回实例对象

function Gcmodel(name,age,job){
  var o = new Object()
  o.name = name 
  o.age = age
  o.job = job
  return o
}
var person1 = Gcmodel('zxy',20,"程序员")
console.log(person1)
2、构造函数:

步骤:创建构造函数==>添加属性==>使用new实例构造函数

function Person(name,age,job){
  this.name=name;
  this.age=age;
  this.job=job;
  this.sayName = function(){
    alert(this.name);
  }
}
var person1 = new Person("dada",1,"web");
console.log(person1)
3、原型模式:

步骤:创建函数对象==>在函数对象原型上添加属性==>使用new实例化

function Person(){
            
}
Person.prototype.name = "zxy"
Person.prototype.age = "18"
var person1 = new Person();
console.log(person1.name)
4、组合式(构造函数+原型)创建:

步骤:创建构造函数==>为构造函数添加原型实例==>使用new实例化

function Person(name,age){
  this.name = name
  this.age = age
}
Person.prototype = {
  constructor:Person,
  sex :"男"
}
var person1 = new Person("zxy",18);
console.log(person1.sex)

18、==和===的区别?

==和=== 相同点:都是比较运算符,用于比较两个值是否相等

不同点:

==:

两者都为简单类型,字符串和布尔值都会转换为数值再比较

简单类型和引用类型比较,对象转换为其原始类型的值再比较

两者都为引用类型,则比较它们是否指向同一个对象

null和undefined相等

===:比较类型是否相等

如果是简单类型,那么直接比较两个值的类型是否相等

如果是引用数据类型,则比较它们各自的内存地址是否相同

总结来说,===用于更精确的比较,而==用来简单的比较

19、类型转换?

分为强制类型转换和自动转换;

强制转换:

强制类型转换就是显示转换,我们可以明确的看到类型发生了转换;

使用Number,String,parseInt,Boolean

转换规则:
Number:

字符串只有数字(直接转为数字)

字符串带字母或汉字的(转为NaN)

空字符串:0

undefined:NaN

Boolean:true(1),false(0)

null:0

object对象类型:都转为NaN(除了只包含一个值的数组)

parseInt:

逐个解析:parseInt("123A3")只解析到123 后边不转换

String:
Boolean:
隐式转换:

定义:就是我们看不到的类型转换,使用运算符来转换

转换规则:

比较运算符:==、!=、>、<、if、while

计算运算符:+、-、*、/、%

自动转换布尔值:在需要布尔值的时候,系统内部会调用Boolean函数

undefined、null、false、+0、-0,NaN、“ ”,会被转换为false

20、promise的理解?

定义:

promise是ES6异步编程的一种解决方案(目前最先进的解决方案是async和await的搭配(ES8),但是它们是基于promise的),从语法上讲,Promise是一个对象或者说是构造函数,用来封装异步操作并可以获取其成功或失败的结果;

中断promise链式:在指定的链式中返回一个pedding状态的promise对象

return new Promise(()=>{});

promise的好处:

1.可以解决回调地狱问题;支持链式调用;

2.Promise 对象提供了简洁的API,使得控制异步操作更加容易(js执行机制导致的异步问题)

promise三个状态:

1、pending等待中,或者进行中,表示还没有得到结果

2、fulfilled:已经完成,表示得到了我们想要的结果,可以继续往下执行

3、rejected 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行

throw:抛出错误,将状态改为rejected;

promise的状态只能修改一次,无论是失败还是成功都会有结果数据,而且都不会再被改变;

promise的回调函数:

1、resolve:表示请求成功了,将状态改为resolved

2、reject:表示请求失败;将状态改为rejected

promise的实例方法:

1、then()得到异步任务的正确结果

2、catch()获取异常信息

3、finally()成功与否都会执行(尚且不是正式标准)

promise的对象方法:

promise.all()并发处理多个异步任务,所有任务都执行完成才能得到结果

promise.race()并发处理多个异步任务,只要有一个任务完成就能得到结果

//1.获取轮播数据列表
function getBannerList(){
  return new Promise((resolve,reject)=>{
    setTimeout(function(){
      resolve('轮播数据')
    },300) 
  })
}
//2.获取店铺列表
function getStoreList(){
  return new Promise((resolve,reject)=>{
    setTimeout(function(){
      resolve('店铺数据')
    },500)
  })
}
//3.获取分类列表
function getCategoryList(){
  return new Promise((resolve,reject)=>{
    setTimeout(function(){
      resolve('分类数据')
    },700)
  })
}
function initLoad(){ 
  Promise.all([getBannerList(),getStoreList(),getCategoryList()])
    .then(res=>{
      console.log(res) 
    }).catch(err=>{
      console.log(err)
    })
} 
initLoad()
async/await:

async和await一般用于处理异步请求;

async:

函数的返回值为promise对象

promise对象的结果有async函数执行的返回值决定

如果返回的是一个非promise类型的数据 return返回的是什么就是promise对象返回的值

如果返回的是promise对象那么返回值就是promise对象的返回结果,状态取决于返回的状态

如果使用throw抛出结果,那么返回值就是抛出的结果,promise对象状态改为rejected

async function main(){
  return 1234
  // return new Promise((resolve,reject)=>{
  //     reject('1234')
  // })
  // 
  throw '12344'
}
let res = main()
console.log(res)
await:

要配合async一起使用;

1、如果右侧返回的是promise对象,那么返回值就是promise对象成功的值

2、如果右侧返回的不是promise对象是其他的值,那么直接将此值作为await的返回值;

3、如果返回的是失败的promise对象,使用try{}catch()进行处理

async function main(){
  let pro =  new Promise((resolve,reject)=>{
    reject('1234')
  })
  try{
    let res = await pro;
  }catch(e){
    console.log(e)
  }

  // console.log(res)
}
main()

21、Ajax和fetch的区别?

相同点:都是请求数据的方法、都有内置API

不同点:

使用:ajax是通过使用XMLHttpRequest对象来向服务器发送异步请求,从服务器获取数据,然后用javascript来操作dom而更新页面;

fetch api是基于promise的设计,返回的是promise对象

监听api:ajax自带了监听进度的api,而fetch没有

内置api:ajax是js内置api,fetch是window的内置api

请求:fetch对于400,500都当成成功的请求,需要封装处理

截止:ajax自带了中途取消请求的api,fetch没有

Ajax实现过程:

1、创建Ajax的核心对象XMLHttpRequest对象

2、通过XMLHttpRequest核心对象的open()方法与服务器建立连接

3、构建请求所需的数据内容,并通过XMLHttpRequest对象的send()方法发送给服务器

4、通过XMLHttpRequest对象提供的onreadystatechange事件来监听服务器端你的通信状态

5、将处理结果更新到HTML页面中

1、创建XMLHttpRequest
const xhr = new XMLHttpRequest();

2、与服务器建立连接
xhr.open(method, url)

3、给服务端发送数据
xhr.send([body])

4、绑定onreadystatechange事件
const request = new XMLHttpRequest()
request.onreadystatechange = function(e){
  if(request.readyState === 4){ // 整个请求过程完毕
    if(request.status >= 200 && request.status <= 300){
      console.log(request.responseText) // 服务端返回的结果
    }else if(request.status >=400){
      console.log("错误信息:" + request.status)
    }
  }
}
request.open('POST','http://xxxx')
request.send()

22、set和map的区别?

Map:

保存键值对,并且能够记住键的原始插入顺序,任何值都可以作为键和值;

类似于对象形式

不允许有重复的键名存在

const map = new Map([["name",1],["age",20]])
map.set("address","河北")
console.log(map.has('name'))
console.log(map.get('name'))
console.log(map.delete('name'))
console.log(map,map.size)
Set:

允许存储任何类型的唯一值;

类似于数组形式;

不允许出现重复值

只能通过迭代器来更改值

const set = new Set([1,2,3])
set.add(4); //添加值
set.delete(1) //删除某个元素
console.log(set)
console.log(set.has(1)) //查找是否存在
console.log(set.size) //获取长度
console.log([...set])  //转换为数组
console.log(Array.from(set))   //转换为数组
const arr = [1,2,5,34,2,5,6]
console.log([...new Set(arr)]) //数组去重--数组元素不存在对象形式;

23、innerTest和testContent的区别?

在 DOM 节点中,innerTest 和 testContent 可能代表不同的属性或者文本内容,具体取决于它们在 HTML 或 JavaScript 中的使用方式。

  1. innerTest:通常不是 DOM 中的标准属性或方法,它可能是你在代码中自定义的属性或者是某个框架或库中的特定属性。如果你在代码中看到了 innerTest,需要查看其所属的上下文,可能是为了描述某个特定的功能或状态。
  2. testContent:通常用于表示 DOM 元素的文本内容,它是 DOM 元素的一个标准属性。可以通过 JavaScript 来获取或修改元素的文本内容,例如:javascript复制代码
// 获取 id 为 "myElement" 的元素的文本内容
let content = document.getElementById("myElement").textContent;

// 修改 id 为 "myElement" 的元素的文本内容
document.getElementById("myElement").textContent = "New content";

总之,在 DOM 节点中,innerTest 可能是某个自定义属性或状态的名称,而 testContent 通常用于表示 DOM 元素的文本内容。具体取决于它们在你的代码中的使用方式和上下文。

24、ES6. proxy?

定义:

Proxy 是一种用于创建代理对象的特殊对象,用于修改某些操作的默认行为,可以理解为在目标对象之前架设了一层拦截,外界对该对象的访问,都必须通过这层拦截,通过这种机制,我们可以对外界的访问进行过滤和改写;

Reflect:

Reflect内部包含proxy的所有行为以及API,是以静态方法的形式存在的,可以让object操作都变成函数行为;

常用的API:

get(target,key):拦截对象属性的读取;通过reflect.get(target,key),进行属性的可读性

set(target,key,val):拦截对象属性的修改;通过reflect.set(target,key,val)进行属性的可修改性;

deleteProperty(target,key):拦截删除对象key的操作;通过reflect.deleteProperty(target,key)来进行对象key的删除拦截

25、判断元素是否在可视区域?

1、offsetTop,scrollTop;
offsetTop:元素的上外边框到包含元素的上内边框的距离
scrollTop:元素中的内容超出元素上边界的高度;

function isKs(el){
  //视口的高度
  const viewportHeight = window.innerHeight ||document.documentElement.clientHeight||document.body.clientHeight 
  const offset =  el.offsetTop; //元素上边框到包含元素的上内边框
  const scrolltop = ducument,documentElement.scrollTop //元素中的内容超出元素上边界的高度
  const top = offset - scrolltop 
  return top <= viewportHeight 
}

26、get请求和post请求的区别?

1、 get请求一般用来获取数据;

post请求一般用于发送数据

2、get因为参数会放在url中,所以隐私性、安全性低,参数内容长度也会在2-8k之间

post请求时没有长度限制的,请求数据放在body中;

3、get请求刷新服务器或者回退没有影响,post请求回退时会重新提交数据请求;

4、get请求可以被缓存,post请求不会被缓存;

5、get请求会被保存在浏览器历史记录当中,post不会,get请求可以被收藏为书签,因为参数在url中;

27、var、let、const区别?

块级作用域:块级作用域由{}包括,let和const具有块级作用域,var不存在;

变量提升:var存在变量提升可以在变量声明之前使用,let和const不具备

重复声明:var声明的变量可以重复声明,后声明的会覆盖前边的变量,let和const不可以;

暂时性死区:在使用let和const声明变量之前,该变量是不可用的,这种语法就叫做暂时性死区;

!# const定义的常量数据时不可以被修改的,如果是普通类型的值是不可以直接被修改的,如果是引用数据类型的值它的内存地址是不可以被修改的,但是里边的数据可以发生变化;

28、排序方法?

冒泡排序: 两两相比,小的在前大的在后,依次类推;重复此操作直到顺序一致

function bubbleSort(arr) {
  const len = arr.length;
  for (let i = 0; i < len - 1; i++) {
    for (let j = 0; j < len - 1 - i; j++) {
      if (arr[j] > arr[j+1]) {        // 相邻元素两两对比
        var temp = arr[j+1];        // 元素交换
        arr[j+1] = arr[j];
        arr[j] = temp;
      }
    }
  }
  return arr;
}

1

简单选择排序:从数组中查找最小的数字,与最前边的数字交换位置,重复该操作直到顺序一致;

function selectionSort(arr) {
  var len = arr.length;
  var minIndex, temp;
  for (var i = 0; i < len - 1; i++) {
    minIndex = i;
    for (var j = i + 1; j < len; j++) {
      if (arr[j] < arr[minIndex]) {     // 寻找最小的数
        minIndex = j;                 // 将最小数的索引保存
      }
    }
    temp = arr[i];
    arr[i] = arr[minIndex];
    arr[minIndex] = temp;
  }
  return arr;
}

快速排序:从数组中随机抽出一个数(一般为数组的第一项),将基准数放在数组中间,然后依次比较,比基准数小的放左边,把基准数大的放右边,循环此操作

function quickSort (arr) {
  const rec = (arr) => {
    if (arr.length <= 1) { return arr; }
    const left = [];
    const right = [];
    const mid = arr[0]; // 基准元素
    for (let i = 1; i < arr.length; i++){
      if (arr[i] < mid) {
        left.push(arr[i]);
      } else {
        right.push(arr[i]);
      }
    }
    return [...rec(left), mid, ...rec(right)]
  }
  return res(arr)
};

插入排序:从右向左依次比较,如果后边比前边小则插入到前边,循环此操作即可;

function insertionSort(arr) {
  const len = arr.length;
  let preIndex, current;
  for (let i = 1; i < len; i++) {
    preIndex = i - 1;
    current = arr[i];
    while(preIndex >= 0 && arr[preIndex] > current) {
      arr[preIndex+1] = arr[preIndex];
      preIndex--;
    }
    arr[preIndex+1] = current;
  }
  return arr;
}
  • 14
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值