js知识点

js知识点汇总

线程和进程是什么?举例说明

进程:cpu分配资源的最小单位(是能拥有资源和独立运行的最小单位)
线程:是cpu最小的调度单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)
例子:比如进程=火车,线程=车厢

一个进程内有多个线程,执行过程是多条线程共同完成的,线程是进程的部分。
一个火车可以有多个车厢
每个进程都有独立的代码和数据空间,程序之间切换会产生较大的开销;线程可以看作轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小。

为什么js是单线程

JS是单线程的原因主要和JS的用途有关,JS主要实现浏览器与用户的交互,以及操作DOM。
如果JS被设计为多线程,如果一个线程要修改一个DOM元素,另一个线程要删除这个DOM元素,这时浏览器就不知道该怎么办,为了避免复杂的情况产生,所以JS是单线程的。

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

js中的数据类型有哪几种?是如何存储值的? 了解包装对象吗?

  • 基本数据类型: Number、String、Boolean、null、undefined、Symbol(es6新增,表示独一无二的值)和Bigint(es10新增)
  • 引用类型:object,Array和Function
  • 原始数据类型:直接存储在栈(stack)中,占据空间小,大小固定,属于被频繁使用数据,所以放入栈中存储。
  • 引用数据类型:同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。如果都存放在栈中,将会影响程序运行的性能。
  • 基础数据类型临时创建的临时对象,称为包装对象。其中 number、boolean 和 string 有包装对象,代码运行的过程中会找到对应的包装对象,然后包装对象把属性和方法给了基本类型,然后包装对象被系统进行销毁。

JS基本和非基本数据类型之间的一些区别?

  • 基本数据类型是不可变的,而非基本数据类型是可变的.
  • 基本数据类型是不可变的,因为它们一旦创建就无法更改,但非基本数据类型刚可更改,意味着一旦创建了对象,就可以更改它.
  • 将基本数据类型与其值进行比较,这意味着如果两个值具有相同的数据类型并具有相同的值,那么它们是严格相等的.
  • 非基本数据类型不与值进行比较.例如,如果两个对象具有相同的属性和值,则它们严格不相等.

对内存泄漏的了解

  1. 理解
  • 定义:程序中已在堆中分配的内存,因为某种原因未释放或者无法释放的问题
  • 简单理解: 无用的内存还在占用,得不到释放和归还,比较严重的时候,无用的内存还会增加,从而导致整个系统卡顿,甚至崩溃。
  1. 生命周期
  • 分配期
    分配所需要的内存,在js中,是自动分配的
  • 使用期
    使用分配的内存,就是读写变量或者对象的属性值
  • 释放期
    不需要时将该内存释放,js会自动释放(除了闭包和一些bug以外)
    内存泄漏就是出现在这个时期,内存没有被释放导致的
  1. 可能出现内存泄漏的原因
  2. 意外的全局变量
//在js中,当引用一个未声明的变量的时候,会在全局对象上创建一个新的变量,在浏览器中全局对象是window。
function foo(arg) {  
  bar = "some text"; //等同于 window.bar = "some text";
  this.foo = "some text"; //等同于 window.foo = "some text";
}
  1. DOM元素清空时,还存在引用
  2. 闭包
  3. 遗忘的定时器或回调函数
  • 如何优化内存泄漏?
  • 全局变量先声明再使用
  • 避免过多使用闭包
  • 注意清除定时器和事件监听器

js中数组合并的方法

  • js数组合并
let arr1 = ['温情', '刘聪']
let arr2 = ['杨和苏', '邓紫棋']
let arr3 = ['周延']
  1. arr1.concat(arr2, ······)

es5 Array.concat() 合并两个数组, 返回新数组,不会改变原数组
arr = arr1.concat(arr2, arr3);
console.log(arr); // [“温情”, “刘聪”, “杨和苏”, “邓紫棋”, “周延”]

  1. […arr1, …arr2,······]

es6 展开运算符(…)
arr = […arr1, …arr2, …arr3];
console.log(arr); // [“温情”, “刘聪”, “杨和苏”, “邓紫棋”, “周延”]

  1. push(…arr)

push 结合 …[] 来实现, 会更改原数组
arr1.push(…arr2, …arr3)
console.log(arr1); // [“温情”, “刘聪”, “杨和苏”, “邓紫棋”, “周延”]

  • 适合两个数组,不适合多个数组的方法
  1. for + push
for(let i in arr2) {
    arr1.push(arr2[i])
}
console.log(arr1);  // ["温情", "刘聪", "杨和苏", "邓紫棋"]
  1. arr1.push.apply(arr1, arr2)
arr1.push.apply(arr1, arr2)
console.log(arr1);  // ["温情", "刘聪", "杨和苏", "邓紫棋"]

合并对象的方法

  • Object.assign()

es6 Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)
Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象。

let obj1 = {name: '温情'}
let obj2 = {age: '22'}
const newObj = Object.assign({}, obj1, obj2);
console.log(newObj);  // {name: "温情", age: "22"}
  • 注意! Object.assign()实行的是浅拷贝,也就是说如果源对象的属性是一个对象,那么目标对象得到的是这个对象的引用
let obj1 = {name: {chinese: '杨和苏', english: 'keyNG'}}
const newObj = Object.assign({}, obj1);
console.log(newObj);  // name: {chinese: "杨和苏", english: "keyNG"}
obj1.name.english = 'pig';
console.log(newObj); // name: {chinese: "杨和苏", english: "pig"}

什么是作用域,什么是作用域链?

  • 规定变量和函数的可使用范围称为作用域
  • 查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作用域链。如果这个变量在自己的作用域中没有,那么它会寻找父级的,直到最顶层。
  • 变量取值到创建这个变量的函数的作用域中取值.但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链.
  • JS中的作用域链主要用于解析变量的值.如果没有这个,在不同的作用域内定义了许多变量,JS很难为变量选择某个值.

JS如何实现异步编程(5种)

  1. 回调函数(callback)
  • 优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。)
  • 缺点:回调地狱,每个任务只能指定一个回调函数,不能 return.
  1. 事件监听
  • 这种思路是说异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。比如一个我们注册一个按钮的点击事件或者注册一个自定义事件,然后通过点击或者trigger的方式触发这个事件。
  1. Promise
  2. Generator
  3. async/await(ES7)

js中的堆内存与栈内存

在js引擎中对变量的存储主要有两种位置,堆内存和栈内存。

栈(stack):由编译器自动分配释放,存放函数的参数值,局部变量等;
堆(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统释放。

和java中对内存的处理类似,栈内存主要用于存储各种基本类型的变量,包括Boolean、Number、String、Undefined、Null以及对象变量的指针,这时候栈内存给人的感觉就像一个线性排列的空间,每个小单元大小基本相等。

堆内存主要负责像对象Object这种变量类型的存储

栈内存中的变量一般都是已知大小或者有范围上限的,算作一种简单存储。而堆内存存储的对象类型数据对于大小这方面,一般都是未知的。个人认为,这也是为什么null作为一个object类型的变量却存储在栈内存中的原因。

因此当我们定义一个const对象的时候,我们说的常量其实是指针,就是const对象对应的堆内存指向是不变的,但是堆内存中的数据本身的大小或者属性是可变的。
而对于const定义的基础变量而言,这个值就相当于const对象的指针,是不可变。

每次使用const或者let去初始化一个变量的时候,会首先遍历当前的内存栈,看看有没有重名变量,有的话就返回错误

使用new关键字初始化的之后是不存储在栈内存中的,new根据构造函数生成新实例,生成的是对象,而不是基本类型。

var a = new String("123")
var b = String("123")
var c = "123"
console.log(a==b,a===b,b==c,b===c,a==c,a===c)
// true false true true true false
console.log(typeof a)
// "object"

我们可以看到new一个String,出来的是对象,而直接字面量赋值和工厂模式出来的都是字符串。但是根据我们上面的分析大小相对固定可预期的即便是对象也可以存储在栈内存的,比如null,为啥这个不是呢?再继续看

var a = new String("123")
var b = new String("123")
console.log(a==b,a===b)
// false false

很明显,如果a,b是存储在栈内存中的话,两者应该是明显相等的,就像null === null是true一样,但结果两者并不相等,说明两者都是存储在堆内存中的,指针指向不一致。

判断js数据类型

  • 用typeof去判断,typeof只能判断基本数据类型,对于引用数据类型,一律返回object,在js中,数组是一种特殊的对象类型, 因此typeof一个数组,返回的是object。
  • 通过instanceof来判断,它不能检测基本数据类型,它是用来判断某个实例是否属于某种类型, 使用它的方式可以用A instanceof B,如果A是B的实例,则返回true,否则返回flase。
  • 用constructor来判断,除了undefined和null之外,其它类型都可以通过constructor来判断,如果声明了一个构造函数,并且把它的原型指向改变了,这种情况下,constructor也不能准确的判断。
  • 通过0bject.prototype.toString,判断一个对象 只属于某种内置类型,但是不能准确的判断一个实例是否属于某种类型。
    原因是==因为实例对象可能会自定义toString方法,把这个方法给覆盖掉,我们可以通过函数. call( )方法,可以在任意值上调用这个方法,帮助我们判断这个值的类型。==

什么是跨域?

  • 同源策略就是用来限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。
  • 同源条件:
  1. 协议相同
  2. 端口相同
  3. 主机相同

跨域解决办法

  1. JSONP
    ==定义:全称json with padding(填充式json)==
  • 在页面上,js脚本,css样式文件,图片这三种资源是可以与页面本身不同源的。jsonp就利用了script标签进行跨域取得数据。
  • JSONP允许用户传递一个callback参数给服务器端,然后服务器端返回数据时会将这个callback参数作为函数名来包裹住JSON数据。这样客户端就可以随意定制自己的函数来自动处理返回的数据了。
  • JSONP只能解决get请求,不能解决post请求。在调用失败的时候不会返回各种HTTP状态码。存在提供jsonp的服务页面有漏洞,即它返回的javascript的内容容易被人控制
<script>
        function callback(data){
            console.log(data);
        }
</script>
<script src="http://localhost:80/?callback=callback"></script>
  • 使用ajax实现跨域:
<script src="http://code.jquery.com/jquery-latest.js"></script> 
  $.ajax({
            url:'http://localhost:80/?callback=callback',
            method:'get',
            dataType:'jsonp', //=> 执行jsonp请求
            success:(res) => {
                console.log(res);
            }
        })
        function callback(data){
            console.log(data);
        }
  1. CORS跨域资源共享:
    ==定义:Cross-origin-resource-sharing(跨域资源共享)==
  • 依附于AJAX,通过添加HTTP Header部分字段请求获取有权限访问的资源。
    浏览器会自动进行CORS通信,实现CORS通信的关键是后端。服务端设置Access-Control-Allow-Origin就可以开启CORS。该属性表示哪些域名跨域访问资源。

主要设置以下几个属性:

  • Access-Control-Allow-Origin //允许跨域的域名
  • Access-Control-Allow-Headers //允许的header类型
  • Access-Control-Allow-Methods //跨域允许的请求方式(GET,POST,PUT,DELETE,OPTIONS)CORS 并不是为了解决服务端安全问题,而是为了解决如何跨域调用资源。
  1. Nginx反向代理
  • 通过nginx配置一个代理服务器将客户机请求转发给内部网络上的目标服务器;
    并将服务器上返回的结果返回给客户端。
  1. webpack(在vue.config.js文件中)中配置webpack-dev-server
devServer: {
        proxy: {
          '/api': {
            target: "http://39.98.123.211", //跨域目标域名
            changeOrigin: true,  //是否跨域
          },
        },
      },
  1. document.domain方法

document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域。这种方法只能解决主域相同的跨域问题。

  1. window.name方法

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

  1. window.postMessage() ==html5新增==
    otherWindow.postMessage(message, targetOrigin, [transfer]);
  • otherWindow:
    其他窗口的一个引用,比如 iframe 的 contentWindow 属性、执行 window.open 返回的窗口对象、或者是命名过或数值索引的 window.frames。
  • message:
    将要发送到其他 window的数据。
  • targetOrigin:
    指定哪些窗口能接收到消息事件,其值可以是 *(表示无限制)或者一个 URI。
  • transfer:
    可选,是一串和 message 同时传递的 Transferable 对象。这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

怎么让对象的一个属性不可被改变

  1. Object.defineProperty()
  • 可以使用Object.defineProperty()方法,让对象的某一个属性不可变,把对象某一属性的writable和configurable设置为false.
let obj = {a:1,b:2};
Object.defineProperty(obj,'c',{
    value:100000,
    writable:false, //当该属性的 writable 键值为 true 时,属性的值才能被赋值操作修改
    configurable:false, //当为true时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。
    })
    obj.c = 282031283
    console.log(obj.c)//100000
  1. object.preventExtensions()
  • 让对象不能添加新属性,可以使用object.preventExtensions()方法。(但是可以修改属性值)
    let obj = {a:1,b:2};
    Object.preventExtensions(obj);
    obj.c = 1000;
    console.log(obj) //{a:1,b:2}

判断一个函数是普通函数还是构造函数

  • 构造函数中this指向new创建的实例。所以可通过在函数内部判断this是否为当前函数的实例进而判断当前函数是否作为构造函数。
    function A(){
        if(this instanceof A){
            console.log('我是构造函数')
        }else{
            console.log('我是普通函数')
        }
    }
    A();
    new A();

JavaScript 中的提升是什么

提升意味着所有的声明都被移动到作用域的顶部。这发生在代码运行之前。

对于函数,这意味着你可以从作用域中的任何位置调用它们,甚至在它们被定义之前。

函数声明

  • 具有特定参数的函数称为函数声明,在JS中创建变量称为声明.
hello();  // Prints "Hello world! " 函数提升
function hello(){ 
 console.log("Hello world! ");
}

函数表达式

  • 当使用表达式创建函数时,称为函数表达式。如:
notHoisted(); // TypeError: notHoisted is not a function
var notHoisted = function() {
  console.log('bar');
};

对于变量,提升有点不同。它在作用域的顶部将 undefined 分配给它们。

console.log(dog);//undefined
var dog = "Spot";
  • ==用var声明一个函数或变量,无论你在哪里声明它,它总是被移动到作用域的顶部==。

js的数据类型的转换都有哪些

在JS中类型转换只有三种情况,分别是:

  • 转换为布尔值(调用Boolean()方法)
  • 转换为数字(调用Number()、parseInt()和parseFloat()方法)
  • 转换为字符串(调用.toString()或者String()方法)
  • ==注:null和undefined没有.toString方法==

介绍 js 有哪些内置对象

  • 一般我们经常用到的如全局变量值NaN、undefined,全局函数如parseInt()、parseFloat()
  • 用来实例化对象的构造函数如Date、Object等,还有提供数学计算的单体内置对象如Math对象.

javascript 创建对象的几种方式?

  • 第一种是工厂模式,工厂模式的主要工作原理是用函数来封装创建对象的细节,从而通过调用函数来达到复用的目的.
function creatPerson(name, age) {
   var obj = new Object();
   obj.name = name;
   obj.age = age;
   obj.sayName = function() {
       window.alert(this.name);
   };
   return obj;
}

但是它有一个很大的问题就是创建出来的对象无法和某个类型联系起来,它只是简单的封装了复用代码,而没有建立起对象和类型间的关系.

  • 第二种是构造函数模式。js中每一个函数都可以作为构造函数,只要一个函数是通过new来调用的,那么我们就可以把它称为构造函数,执行构造函数首先会创建一个对象,然后将对象的原型指向构造函数的prototype属性,然后将执行上下文中的this指向这个对象,最后再执行整个函数,如果返回值不是对象,则返回新建的对象。因为this的值指向了新建的对象,因此我们可以使用this给对象赋值,
function Person(name, age) {
   this.name = name;
   this.age = age;
   this.sayName = function() {
       window.alert(this.name);
   };
}

构造函数模式相对于工厂模式的优点是,==所创建的对象和构造函数建立起了联系==,因此我们可以通过原型来识别对象的类型。
但是构造函数存在一个缺点就是==造成了不必要的函数对象的创建==,因为在js中函数也是一个对象,因此如果对象属性中如果包含函数的话,那么每次我们都会新建一个函数对象,浪费了不必要的内存空间,因为函数是所有的实例都可以通用的。

  • 第三种模式是原型模式,因为每一个函数都有一个prototype属性,这个属性是一个对象,它包含了通过构造函数创建的所有实例都能共享的属性和方法.因此我们可以使用原型对象来添加公用属性和方法,从而实现代码的复用。这种方式相对于构造函数模式来说,解决了函数对象的复用问题
function Person() {}
Person.prototype = {
   constructor : Person,
   name : "Ning",
   age : "23",
   sayName : function() {
       window.alert(this.name);
   }
};

但是这种模式也存在一些问题,==一个是没有办法通过传入参数来初始化值,另一个是如果存在一个引用类型如Array这样的值,那么所有的实例将共享一个对象,一个实例对引用类型值的改变会影响所有的实例。==

  • 第四种模式是组合使用构造函数模式和原型模式,这是创建自定义类型的最常见方式。因为构造函数模式和原型模式分开使用都存在一些问题,因此我们可以组合使用这两种模式,通过构造函数来初始化对象的属性,通过原型对象来实现函数方法的复用。这种方法很好的解决了两种模式单独使用时的缺点,
function Person(name, age) {
   this.name = name;
   this.age = age;
}
Person.prototype = {
   constructor : Person, 
   sayName : function() {
       window.alert(this.name);
   }
};

==但是有一点不足的就是,因为使用了两种不同的模式,所以对于代码的封装性不够好。==

  • 第五种模式是动态原型模式,这一种模式将原型方法赋值的创建过程移动到了构造函教的内部,通过对属性是否存在的判断,可以实现仅在第一次调用函数时对原型对象赋值一次的效果。
    ==这一种万式很好地对上面的混合模式进行了封装。==
  • 第六种模式是寄生构造函数模式,这一种模式和工厂模式的实现基本相同,我对这个模式的理解是
    它主要是基于一个已有的类型,在实例化时对实例化的对象进行扩展。这样既不用修改原来的构造函数也达到了扩展对象的目的。
    ==它的一个缺点和工厂模式一样,无法实现对象的识别==

js获取原型的方法?

  • p.proto
  • p.constructor.prototype
  • Object.getPrototypeOf

什么是闭包,为什么要用它?

说白了就是函数嵌套函数,内部函数能够访问外部函数的变量,且在外部被执行,就产生了闭包。常见的构造方法,是在一个函数内部定义另外一个函数。内部函数可以引用外层的变量,把该内部函数通过外部函数return到外部去执行,就形成了闭包。外层变量不会被垃圾回收机制回收。

  • 闭包的原理是作用域链,所以闭包访问的上级作用域中的变量是个对象,其值为其运算结束后的最后一个值。
  • 优点:
  • 第一个用途:创建私有变量,避免全局变量的污染
  • 第二个用途:可以使已经运行结束的函数中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收
  • 缺点:长期占用内存,容易造成内存泄漏。
// 闭包实例
function makeFunc() {
   var name = "Mozilla";
   function displayName() {
       console.log(name); 
   }
   return displayName;
}
var myFunc = makeFunc();
myFunc();   //输出Mozilla

应用场景:

  • 防抖(清除旧定时器,开始新的定时器)、节流(上一个定时器没有结束,则return 不执行新的定时器)
  • 函数柯里化

三种事件模型是什么?

事件是用户操作网页时发生的交互动作或者网页本身的一些操作,现代浏览器一共有三种事件模型

==DOMO级模型==:这种模型不会传播,所以没有事件流的概念,但是现在有的浏览器支持以冒泡的方式实现,它可以在网页中直接定义监听函数,也可以通过js属性来指定监听函数。这种方式是所有浏览器都兼容的

==IE事件模型==:在该事件模型中,一次事件共有两个过程,事件处理阶段,和事件冒泡阶段,事件处理阶段会首先执行目标元素绑定的监听事件。然后是事件冒泡阶段,冒泡指的是事件从目标元素冒泡到document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过attachEvent来添加监听函数,可以添加多个监听函数,会按顺序依次执行。

==DOM2级事件模型==:在该事件模型中,一次事件共有三个过程,第一个过程是事件捕获阶段。捕获指的是事件从document一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。后面两个阶段和IE事件模型的两个阶段相同。这种事件模型,事件绑定的函数是addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行。

简述javascript中this的指向?

第一准则是:==this永远指向函数运行时所在的对象,而不是函数被创建时所在的对象==

  • 普通的函数调用,函数被谁调用,this就是谁
  • 构造函数的话,如果不用new操作符而直接调用,那即this指向window.用new操作符生成对象实例后,this就指向了新生成的对象
  • 匿名函数或不处于任何对象中的函数指向window。
  • 如果是call,apply等,指定的this是谁,就是谁

改变this的指向

  • 使用ES6的箭头函数
  • 在函数内部使用_this = this
  • 使用apply、call、bind
  • new实例化一个对象

解释一下原型和原型链?

原型:js中一切皆对象,对象都有一个隐式的属性proto,它指向该对象的原型prototype(原型对象)

每一个函数有一个prototype的属性,当他作为构造函数的时候,它实例化出来的函数会有一个proto的属性,当访问一个对象的某个属性或者方法的时候,会先在这个对象自身查找,如果没有找到,则会去它的proto隐式原型上查找,也就是它构造函数的prototype,如果还没有找到就会再在构造函数的prototype的proto中查找,直到找到或者返回undefined,这个链式查找的过程,我们称为原型链。

深拷贝、浅拷贝、以及如何实现?

  • 浅拷贝对基本数据类型来说拷贝的是值,但对引用数据类型来说拷贝的只是引用,深拷贝针对的是引用数据类型,拷贝的是值
  • 浅拷贝:使用object.assign只拷贝地址,它是将数据中的所有数据引用下来,指向同一个存放地址,拷贝后的数据修改后,会影响到原数据中的对象数据。
  • 深拷贝:JSON.parse(JSON.Stringify(…)),递归拷贝每一层对象是内容拷贝,将数据中的所有数据都拷贝下来,对拷贝后的数据进行修改,不会影响到原数据.但是会丢失函数、会把时间对象变成字符串、会把正则对象变成空对象。ES6的扩展运算符(…)仅第一层是深拷贝
  • 在深拷贝中,原始对象不与新对象共享相同的属性,而在浅拷贝中,它们具有相同的属性.
    递归:递归就是一个函数调用其本身,通过栈来实现.每执行一个函数,就新建一个函数栈.
//di
function deepClone(obj) {
  var objClone = Array.isArray(obj) ? [] : {};
  if (obj && typeof obj === "object") {
      for (key in obj) {
          if (obj.hasOwnProperty(key)) {
          if (obj[key] && typeof obj[key] === "object") {
              objClone[key] = deepClone(obj[key]);
          } else {
              objClone[key] = obj[key];
          }
          }
      }
  }
      return objClone;
}

DOM事件流和事件委托?

  1. 捕获阶段
  2. 目标阶段
  3. 冒泡阶段
  • 捕获阶段:在事件冒泡的模型中,捕获阶段不会响应任何事件;
  • 目标阶段:目标阶段就是指事件响应到触发事件的最底层元素上;
  • 冒泡阶段:冒泡阶段就是事件的触发响应会从最底层目标一层层地向外到最外层(根节点),事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层
  • 事件流描述的是从页面中接受事件的顺序,IE和网景推出了两个正好相反的概念,IE推出的是冒泡流,从下到上,网景则是事件捕获流,从上到下。
  • 首先通过addEventListener方法给元素添加点击事件,前两个参数分别是点击事件的名称和执行的回调,第三个参数就是是否开启捕获,确定事件发生的阶段,默认是false,也就是冒泡流。
  • 事件委托,一般来说,会把一个或一组元素的事件委托到它的父元素上或者更外层元素上,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。
    ==优点==是可以大大的减少dom操作。另外,如果在每个子元素中都绑定事件处理函数,不仅内存开销大,而且新增的子元素也不会自动添加事件处理函数,那么使用事件委托就可以对新的子元素也能捕获到事件。

ajax是什么?以及如何去创建它?

  • ==ajax==就是用来实现客户端与服务器端的异步通信效果,实现页面的局部刷新,标准浏览器是通过XMLHttpRequest,IE浏览器则是通过ActiveXObject来实现异步通信的效果。

创建Ajax:

  1. 创建XMLHttpRequest对象,也就是创建一个异步调用对象
var xhr = new XMLHttpRequest();
  1. 创建一个新的HTTP请求,并指定该请求的方法、URL及是否异步
xhr.open('get','example.php',false);
  1. 设置响应HTTP请求状态变化的函数
  • onreadystatechange事件中readyState属性等于4。响应的HTTP状态为200(OK)或者304(Not Modified)。
  1. 发送HTTP请求
xhr.send(data);
  1. 获取异步调用返回的数据
  2. 使用JS实现DOM的局部刷新

==注意==:

  • 页面初次加载时,尽量在web服务器一次性输出所有相关的数据,只在页面加载完成之后,用户进行操作时采用ajax进行交互。
  • 同步ajax在IE上会产生页面假死的问题。所以建议采用异步ajax。
  • 尽量减少ajax请求次数
  • ajax安全问题,对于敏感数据在服务器端处理,避免在客户端处理过滤。对于关键业务逻辑代码也必须放在服务器端处理。

防抖和节流

//防抖:事件被调用后,在执行之前无论被调用多少次都会从头开始计时。
//节流:不管事件被调用多少次,总是按规定时间间隔执行。
//防抖
function debounce(fn,time) {
var timer;
return function() {
    if (timer) {
        clearTimeout(timer)
        timer = null;
    }
    timer = setTimeout(() => {
        clearTimeout(timer)
        timer = null;
        fn();
    }, time);
  }
}

//节流
function throttle(fn, time) {
    var timer;
    return function() {
        if (timer) return;
        timer = setTimeout(() => {
            clearTimeout(timer)
            timer = null;
            fn();
    }, time);
  }
}

同步和异步的区别,分别列举一个同步和异步的例子?

同步会阻塞代码 异步不会
alert是同步 setTimeout是异步

前端使用异步的场景

  1. 定时任务:setTimeout,setInterval
  2. 网络请求:ajax请求,动态img加载
  3. 事件绑定

全局函数eval()有什么作用?

eval()只有一个参数,如果传入的参数不是字符串,它直接返回这个参数。如果参数是字符串,它会把字符串当成javascript代码进行编译。如果编译失败则抛出一个语法错误( syntaxError )异常。如果编译成功,则开始执行这段代码,并返回字符串中的最后一个表达式或语句的值,如果最后一个表达式或语句没有值,则最终返回undefined。如果字符串抛出一个异常,这个异常将把该调用传递给eval()。

原生对象和宿主对象?

  • 原生对象是ECMAScript规定的对象,所有内置对象都是原生对象,比如Array、Date、RegExp等,它们也被称为全局对象,因为如果使用JS,内置对象不受运行环境影响;
  • 宿主对象是宿主环境比如浏览器等规定的对象,用于完善是ECMAScript的执行环境,比如Document、Location,Navigator等。这意味着它们在不同的环境下是不同的.

get和post有什么区别?

都是HTTP协议中的请求方法。底层实现都是基于TCP/IP协议。

  • GET是通过明文发送数据请求,而POST是通过密文;
  • GET传输的数据量有限,因为url的长度有限,POST则不受限;
  • GET请求的参数只能是ASCII码,所以中文需要URL编码,而POST请求传参没有这个限制
  • GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http、header和data—并发送出去,服务器响应200(返回数据);
    而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

请指出document.onload和document.ready两个事件的区别?

执行时间

  • 一是ready,表示文档结构(dom树结构)已经加载完成(不包含图片等非文字媒体文件),
  • 二是onload,表示页面包含图片等文件在内的所有元素都加载完成。

执行个数不同

  • window.onload不能同时编写多个,如果有多个window.onload方法,只会执行一个
  • document.ready()可以同时编写多个,==并且都可以得到执行==

请解释JSONP的工作原理,以及它为什么不是真正的AJAX?

  • JSONP(JSON with Padding)是一个简单高效的跨域方式,HTML中的script标签可以加载并执行其他域的javascript,于是我们可以通过script标记来动态加载其他域的资源。例如我要从域A的页面pageA加载域B的数据,那么在域B的页面pageB中我以JavaScript的形式声明pageA需要的数据,然后在pageA中用script标签把pageB加载进来,那么pageB中的脚本就会得以执行。JSONP在此基础上加入了回调函数,pageB加载完之后会执行pageA中定义的函数,所需要的数据会以参数的形式传递给该函数。JSONP易于实现,但是也会存在一些安全隐患,如果第三方的脚本随意地执行,那么它就可以篡改页面内容,截获敏感数据。但是在受信任的双方传递数据,JSONP是非常合适的选择。
  • AJAX是不跨域的,而JSONP是一个是跨域的,还有就是二者接收参数形式不一样!
  • JSONP的主要优势在于对浏览器的支持较好;虽然目前主流浏览器支持CORS,但IE10以下不支持CORS。
  • JSONP只能用于获取资源(即只读,类似于GET请求);CORS支持所有类型的HTTP请求,功能完善。(这点JSONP被完虐,但大部分情况下GET已经能满足需求了)
  • JSONP的错误处理机制并不完善,我们没办法进行错误处理;而CORS可以通过onerror事件监听错误,并且浏览器控制台会看到报错信息,利于排查。
  • JSONP只会发一次请求;而对于复杂请求,CORS会发两次请求。

通过new创建一个对象的时候,构造函数内部有哪些改变?

function Person(){}
Person.prototype.friend =[];
Person.prototype.name = "";
var a = new Person();
a.friend[0] = '杨爽';
var b = new Person();
console.log(b.friend); //Array ["杨爽”]
  • 创建一个空对象,并且this变量引用该对象,同时还继承了该函数的原型。
  • 属性和方法被加入到this 引用的对象中。
  • 新创建的对象由this所引用,并且最后隐式的返回this。

如何防范CSRF攻击,XSS攻击?

  • xSS攻击的防范
  • HttpOnly 防止劫取 Cookie
  • 输入检查-不要相信用户的所有输入
  • 输出检查-存的时候转义或者编码
  • CSRF攻击的防范
  • 验证码
  • Referer Check
  • 添加token验证

箭头函数与普通函数的区别?

  • 箭头函数是在es6或更高版本中编写函数表达式的简明方法。
  • 箭头函数是匿名函数,不能作为构造函数,不能使用new
  • 箭头函数不绑定arguments super 或 new.target关键字,取而代之用展开运算符代替
// 常规函数
function test1(a){
  console.log(arguments);
}
test(1) // 1
// 箭头函数
var test2 = (a) =>{
  console.log(arguments)
}  
//ReferenceError: arguments is not defined 
let test3 = (...a) =>{
  console.log(a[1])
}  
test3(33,22,44); // 22
  • 箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值,箭头函数的this始终指向函数定义时的this,而非执行时
  • 箭头函数通过call()或apply()方法调用一个函数时,只传入了一个参数,对 this并没有影响.
  • 箭头函数没有prototype原型属性.
  • 返回对象字面量需要用小括号包裹起来
  • 如果箭头函数被非箭头函数包含,则this绑定的是最后一层非箭头函数的this,否则,this为undefined
 let fun5 = ()=> ({ foo: x }) 
 //如果 x => { foo: x } //则语法出错

说一下js继承?(6种)

  • ES5中的继承有:
  • 原型链继承:所有对象都公用一份原型属性和方法,对一个类的修改会影响其他类
  • 借用构造函数继承:每次继承都会把父类的所有属性方法全部拷贝一份,而对于公用的方法重复拷贝会浪费内存
function SuperType(name) {
   this.name = name;
   this.sayName = function() {
       window.alert(this.name);
   };
}
function SubType(name, age) {
   SuperType.call(this, name); //在这里借用了父类的构造函数
   this.age = age;
}
  • 组合继承:是结合两种继承方式,用构造函数方式继承属性,原型链方式继承方法
function SuperType(name) {
   this.name = name;
}
SuperType.prototype = {
   sayName : function() {
       window.alert(this.name);
   }
};
function SubType(name, age) {
   SuperType.call(this, name); //在这里继承属性
   this.age = age;
}
SubType.prototype = new SuperType(); //这里继承方法
  • ES6有class继承:
  • class就相当于ES5中的构造函数
  • class中定义的方法后不能加function ,全部定义在class的prototype属性中
  • class只能定义方法,不能定义对象变量等???
  • class默认为严格模式
  • 原型继承了解吗?我给你个场景,有一个FUNA,FUNB,让B继承A,用原型链怎么B继承A的属性?
  function Person(){    
    this.inside = 'inside'    
    this.saySecret = function(){      
      console.log(this.inside)    
    }  
  }
  Person.prototype.hello = "hello"; 
  Person.prototype.sayHello = function(){ 
    console.log(this.hello);  
  }     
  function Child(){}  
  Child.prototype = new Person()   
  let child = new Child()  
  child.sayHello() // hello  
  child.saySecret()// inside  
  console.log(child.inside) // inside 

其他几类:原型式继承、寄生式继承、寄生组合式继承。

JS 中的主要有哪几类错误?

JS有三类的错误:

  • 加载时错误:加载web页面时出现的错误(如语法错误)称为加载时错误,它会动态生成错误。
  • 运行时错误:由于滥用HTML语言中的命令而导致的错误。
  • 逻辑错误:这些错误是由于对具有不同操作的函数执行了错误的逻辑而导致的

JS中的Array.splice()和Array.slice()方法有什么区别?

slice和splice虽然都是对于数组对象进行截取,但是二者还是存在明显区别,
函数参数上slice和splice第一个参数都是截取开始位置,slice第二个参数是截取的结束位置(不包含)而splice第二个参数(表示这个从开始位置截取的长度),
slice不会对原数组产生变化,而splice会直接剔除原数组中的截取数据!

undefined,null 和 undeclared 有什么区别?

  • null表示”没有对象”,即该处不应该有值,转为数值时为0。典型用法是:
    作为函数的参数,表示该函数的参数不是对象。
    作为对象原型链的终点。
    typeof(null) == object
    typeof(undefined) == undefined
  • undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义,转为数值时为NaN。典型用法是:
    变量被声明了,但没有赋值时,就等于undefined。
    调用函数时,应该提供的参数没有提供,该参数等于undefined。
    对象没有赋值的属性,该属性的值为undefined。
    函数没有返回值时,默认返回undefined。
  • undeclared: js语法错误,没有申明直接使用,js无法找到对应的上下文。

JS中的高阶函数?

  • 高阶函数是JS函数式编程的最佳特性。它是以函数为参数并返回函数作为结果的函数。一些内置的高阶函数是map、filter、reduce等等。

区分声明函数和表达式函数?

// 声明函数
function hello() {
  return "HELLO"
}
// 表达式函数
var h1 = function hello() {
  return "HELLO"
}

JS中的严格模式是什么以及如何启用以及作用

  • 严格模式是在代码中引入更好的错误检查的—种方法。
  • 当使用严格模式时,不能使用隐式声明的变量,或为只读属性赋值,或向不可扩展的对象添加属性
  • 可以通过在文件,程序或函数的开头添加”use strict”来启用严格模式
  • ==作用==
  1. 消除js不合理,不严谨地方,减少怪异行为
  2. 消除代码运行的不安全之处
  3. 提高编译器的效率,增加运行速度
  4. 为未来的js新版本做铺垫。

event loop(事件循环、事件轮询)的机制,画图?

  • js在执行过程中存在执行栈:
  • 什么是执行栈:当我们调用一个方法的时候,js会生成一个与这个方法对应的执行环境(context),又叫执行上下文。这个执行环境中存在着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。 而当一系列方法被依次调用的时候,因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。
  • 当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。js是单线程,但是它有个事件队列机制来处理异步操作
  • 事件队列中有包含宏任务和微任务。像常用的setTimeout,setInterval就是宏任务,Promise()的.then(()=>{})/.catch(()=>{})/.finally(()=>{}) 回调都是微任务。js先执行主线程任务,然后看如果有可以执行的微任务就会被执行,再看如果有宏任务就被执行,执行完继续主线程任务-然后就一遍一遍这循环执行,这也就是它的事件循环机制

event loop的执行原理如下:

  • 同步代码,一行一行执行,是放在call stack(调用堆栈)中执行的
  • 遇到异步代码,先记录下在web API中,等待时机(定时、网络请求等)
  • 时机到了,就移动到callback queue (回调队列)中
  • 如果call stack为空(即同步代码执行完毕) event loop开始工作
  • 轮询查找callback queue,如有则移动到call stack执行
  • 然后继续轮询查找,直到完成
  1. DOM事件和event loop
  • js是单线程的
  • 异步(setTimeout、ajax等)使用回调,是基于event loop的DOM事件也使用回调,也基于event loop

什么是宏任务和微任务,两者有什么区别?

  • 宏任务:setTimeout、setInterval、requestAnimationFrame(node没有)、setImmediate(浏览器没有)
  • 微任务:promise(语法糖:async/await).then catch finally、MutationObserver(监视对DOM树所做的更改 node没有)、process.nextTick(比setTimeout更严格的延迟调用 允许用户处理error,清除不需要的资源,或者在事件循环前再次尝试请求 浏览器没有)
  • ==微任务执行时机要比宏任务早==
  • 宏任务和微任务的区别?
    宏任务:DOM渲染后触发,如setTimeout
    微任务:DOM渲染前触发,如promise

解释一下什么是promise?

promise是js中的一个对象,用于生成可能在将来产生结果的值.值可以是已解析的值,也可以是说明为什么未解析该值的原因.

  • promise 可以有三种状态:
  • pending:初始状态,既不是成功也不是失败
  • fulfilled:意味着操作完全成功
  • rejected:意味着操作失败
    一个等待状态的promise对象能够成功后返回一个值,也能失败后带回一个错误
    当这两种情况发生的时候,处理函数会排队执行,通过then方法会被调用.

promise有哪三种状态?如何变化

  • promise有三种状态:pending、resolved、rejected
  • pending->resolved、pending->rejected变化是不可逆的
  • 状态的表现:
    pending状态,不会触发then和catch
    resolved状态,会触发后续的 then 回调函数
    rejected状态,会触发后续的catch回调函数
  • then和catch改变状态
    then正常返回resolved,里面有报错则返回rejected
    catch正常返回resolved,里面有报错则返回rejected

async/await ?

async/await 是同步语法,解决异步回调callback hell问题,promise then catch链式调用,但也是基于回调函数的。

async/await和 promise的关系:

  • async/await 是解决异步回调的,但和promise并不互斥,两者相辅相成。
  • 执行async函数,返回的是promise对象
  • await 相当于promise的then
  • try…catch可捕获异常,代替了promise的catch

event loop 和 DOM渲染

  • js是单线程的,而且和DOM渲染共用一个线程
  • js执行的时候,得留一些时机供DOM渲染

执行流程:

  • call stack空闲
  • 尝试 DOM渲染
  • 触发event loop
  • 每次call stack 清空(即每次轮询结束),即同步任务执行完
  • 都是DOM重新渲染的机会,DOM结构如有改变则重新渲染
  • 然后再去触发下一次event loop

点击穿透

假如页面上有两个元素A和B。B元素在A元素之上。我们在B元素的touchstart事件上注册了一个回调函数,该回调函数的作用是隐藏B元素。我们发现,当我们点击B元素,B元素被隐藏了,随后,A元素触发了click事件。

这是因为在移动端浏览器,==事件执行的顺序是touchstart > touchend > click==。而click事件有300ms的延迟,当touchstart事件把B元素隐藏之后,隔了300ms,浏览器触发了click事件,但是此时B元素不见了,所以该事件被派发到了A元素身上。如果A元素是一个链接,那此时页面就会意外地跳转。

var、let、const、function的总结

var

  • 允许二次定义
var x = 10;console.log(x);//10
var x = 20;console.log(x);//20
  • 存在变量提升
var x = 10;
function test(){
  console.log(x);//undefined
  var x = 10;
}
test();

解释:变量创建步骤细分为:创建>初始化>赋值 当调用test函数,进入一个执行环境,收集变量对象(变量,test函数内的函数),提升到该环境顶端,创建var定义的变量x,同时初始化为undefined。 所以var变量提升包括创建和初始化undefined。

  • 污染全局作用域
for(var i =1;i<5;i++){}
console.log(window.i);//5
  • 隐式变量不存在提升
console.log(a);//Uncaught ReferenceError: a is not defined
a = 'aaa';

let

  • 存在块级作用域
{ let x = 10;} console.log(x);//x is not defined
  • 暂时性死区
    ==暂时性死区:变量必须先定义再使用。==
var x =10;
function test(){
  console.log(x);//Cannot access 'x' before initialization
  let x = 20;
}
test();

打印x报错,提示想要访问x必须先初始化。 当调用test函数,进入一个执行环境,收集变量对象(变量,test函数内的函数),提升到该环境顶端,创建let定义的变量x,但let定义的变量只有[创建]阶段提升了,[初始化]阶段没提升,而没初始化就不给访问,报错。 let 存在提升,只不过是[创建]阶段提升了。

  • 解决延迟打印问题
for(var i=1;i<5;i++){
  setTimeout(()=>{
      console.log(i);
      },1000);
  }//打印4个5
for(let i=1;i<5;i++){
  setTimeout(()=>{
      console.log(i);
      },1000);
  }//打印1 2 3 4

for循环执行了4次,一共创造了4个执行环境,每个执行环境都有对应的i,但是var定义的i没有块级作用域的概念,所以i都是定义在全局上的,而let定义的i都是保存在块级作用域内的。

  • let,const不允许重复定义
{ let x = 5;let x = 10;}//Uncaught SyntaxError: Identifier 'x' has already >been declared
{let x = 5;var x =10;}//Uncaught SyntaxError: Identifier 'x' has already >been declared
{let x = 5;const x =10;}//Uncaught SyntaxError: Identifier 'x' has already >been declared
{const x = 5;let x = 5;}//Uncaught SyntaxError: Identifier 'x' has already >been declared
  • let用于创建一个可变变量,可变变量是像var这样的普通变量,可以任意次数地更改。

const

  • const声明必须带着初始化
const x;
x = 5;
console.log(x);//Uncaught SyntaxError: Missing initializer in const declaration
  • const只能初始化一次
const x = 10;
x = 5;
console.log(x);//Uncaught TypeError: Assignment to constant variable.

如果const保存着对象,那么修改对象的值是可行的。

const obj = {
  name:'san',
  age:22
};
obj.age = 100;
console.log(obj.age);//100
  • 关于const的变量提升
var x = 10;
function test(){
  console.log(x);
  const x = 45;
}
test(); //Uncaught ReferenceError: Cannot access 'x' before initialization

const只有[创建]和[初始化],当进入执行上下文,收集变量对象时,只有[创建]这个阶段被提前了.所以提示未初始化不能访问x.
用于创建一个不可变变量。不可变变量是指其值在程序的整个生命周期中永不改变的变量。

function

  • 函数提升
function test(){
    fn();//fn被执行了
    console.log(fn);//fn(){console.log('fn被执行了')}
    function fn(){
    console.log('fn被执行了');
    }
  }
test();

函数提升是函数名和函数体都提升了。

  • 重复定义函数
    重复定义的函数还是使用原来分配的内存。
function test(){
  console.log('test1');        
  }
function test(){
  console.log('test2');
  }
test();//test2
  • 变量提升和函数提升
    函数提升优先级比变量提升要高,且不会被变量声明覆盖,但是会被变量赋值覆盖。
console.log(a);//ƒ a(){}
var a = 10;
function a(){}
console.log(a);//10

等同于

function a(){}
var a;
console.log(a);//ƒ a(){}
a = 10;
console.log(a);//10

js反转一个字符串

'hello world'.split('').reverse().join('') //reverse关键
//首先将字符串拆分为数组,然后反转数组,最后将字符连接起来形成字符串.
  • 第二种: 使用循环:首先,计算字符串的字符数,然后对原始字符串应用递减循环,该循环从最后一个字符开始,打印每个字符,直到count变为零.

Promise的all和race(修改)

all是可迭代对象里面所有的都变成resolved才会执行下一步,race是只要有一个resolved,就会执行下一步

判断一个对象是否为空的方法:

  • 使用ES6新增语法:Object.keys(testObj)返回一个array类型,里面是testObj中用户定义的键值。
  • 使用JSON.stringify
if(JSON.stringify(testObj)=='{}'){
   console.log('是空对象!') 
}
  • 使用for…in遍历,能够遍历到任何内容的就不为空

为什么0.1 + 0.2 != 0.3

计算时,js解释器将十进制0.1转换为2进制的时候,二进制的表达是无穷的,所以计算结果是不精确的

// 将0.1转换成二进制
console.log(0.1.toString(2)); 
// 0.0001100110011001100110011001100110011001100110011001101

Number() 的存储空间是多大

  • 2的53次方,如果超出的话,会发生截断

构造函数和普通函数的区别

  • 构造函数也是一个普通函数,创建方式和普通函数一样,但构造函数习惯上首字母大写
  • 调用方式不一样
  • 构造函数的执行流程以 new Func()为例
  1. 立刻在堆内存中创建一个新的对象 {}
  2. 将新建的对象设置为函数中的this Func.call和apply
    ({})
  3. 逐个执行函数中的代码 {}.name=xxx; {}.methods = xxx
  4. 将新建的对象作为返回值 return {}
function person(){}
let per = person()
console.log(per) // 返回值是"undefined" 
function Person(){}
let per = new Person()
console.log(per) // 返回值是"[object object]"对象

正则表达式

  • 常用的正则表达式方法有:
  1. search()方法
  • 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,并返回子串的起始位置。
str.search(/Runoob/i); // i表示不区分大小写
  1. replace()方法
  • 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
var txt = str.replace(/microsoft/i,"Runoob");
  1. test()方法
  • 用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false。
var patt = /e/;
patt.test("The best things in life are free!"); // true

Array.map和Array.forEach

Array.map()和Array.forEach((value,index,array)=>{})都不会改变原数组
map返回新的处理后的对象,而forEach会在遍历的时候做一些其他的事情

说说写JavaScript的基本规范?

  1. 不要在同一行声明多个变量
  2. 使用 ===或!==来比较true/false或者数值
  3. switch必须带有default分支
  4. 函数应该有返回值
  5. for if else 必须使用大括号
  6. 语句结束加分号
  7. 命名要有意义,使用驼峰命名法

jQuery使用建议

  • 尽量减少对dom元素的访问和操作
  • 尽量避免给dom元素绑定多个相同类型的事件处理函数,可以将多个相同类型事件处理函数合并到一个处理函数,通过数据状态来处理分支
  • 尽量避免使用toggle事件

JSON了解

  • 全称:JavaScript Object Notation
  • JSON中对象通过“{}”来标识,一个“{}”代表一个对象,如{“AreaId”:”123”},对象的值是键值对的形式(key:value)。JSON是JS的一个严格的子集,一种轻量级的数据交换格式,类似于xml。数据格式简单,易于读写,占用带宽小。

两个函数:

  • JSON.parse(str)
    解析JSON字符串 把JSON字符串变成JavaScript值或对象
  • JSON.stringify(obj)
    将一个JavaScript值(对象或者数组)转换为一个 JSON字符串

页面编码和被请求的资源编码如果不一致如何处理?

  • 若请求的资源编码,如外引js文件编码与页面编码不同。
  • 可根据外引资源编码方式定义为 charset=”utf-8″或”gbk”。
    比如:http://www.yyy.com/a.html 中嵌入了一个http://www.xxx.com/test.js
    a.html 的编码是gbk或gb2312的。 而引入的js编码为utf-8的 ,那就需要在引入的时候
<script src="http://www.xxx.com/test.js" charset="utf-8"></script>

模块化开发怎么做?

模块化开发指的是在解决某一个复杂问题或者一系列问题时,依照一种分类的思维把问题进行系统性的分解。
模块化是一种将复杂系统分解为代码结构更合理,可维护性更高的可管理的模块方式。

  • 对于软件行业:系统被分解为一组高内聚,低耦合的模块。
  1. 定义封装的模块
  2. 定义新模块对其他模块的依赖
  3. 可对其他模块的引入支持。在JavaScript中出现了一些非传统模块开发方式的规范。
  • CommonJS的模块规范
  • AMD(Asynchronous Module Definition)
  • CMD(Common Module Definition)
  • AMD是异步模块定义,所有的模块将被异步加载,模块加载不影响后边语句运行。

AMD(Asynchronous Module Definition)、CMD(Common Module Definition)规范区别?

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。

  • 区别:
  1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。
  2. CMD 推崇依赖就近,AMD 推崇依赖前置。
  3. AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。
// CMD
define(function(require, exports, module) {
    var a = require('./a')
    a.doSomething()
    // 此处略去 100 行
    var b = require('./b') // 依赖可以就近书写
    b.doSomething()
})
// AMD 默认推荐
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
    a.doSomething();
    // 此处略去 100 行
    b.doSomething();
})

requireJS的核心原理是什么?

如何动态加载的?如何避免多次加载的?如何缓存的?

  • 核心是js的加载模块。
  • 通过正则匹配模块以及模块的依赖关系,保证文件加载的先后顺序。
  • 根据文件的路径对加载过的文件做了缓存。

call和apply和bind之间的异同

call()方法和apply()方法和bind()的作用相同,动态的修改当前函数内部环境对象this的指向。都不会修改原先函数的this指向.

apply定义:调用一个函数, 其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数

  • 语法: fun.apply(thisArg, [argsArray])
  1. thisArg:在 fun 函数运行时指定的 this 值。需要注意的是,指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。
  2. argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。浏览器兼容性请参阅本文底部内容。

call定义:类似apply

  • 语法:fun.call(thisArg, arg1, arg2)
  1. call方法接受的是若干个参数列表

bind定义:创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。

不同点:

  1. 执行方式不同:
  • call和apply是改变后页面加载之后就立即执行,是同步代码。
  • bind是异步代码,改变后不会立即执行;而是返回一个新的函数。
  1. 传参方式不同:
  • call和bind传参是一个一个逐一传入,不能使用剩余参数的方式传参。
  • apply可以使用数组的方式传入的,只要是数组方式就可以使用剩余参数的方式传入。

回流与重绘

当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。
每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树。
完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘

DOM操作

  1. 创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
  1. 添加、移除、替换、插入
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子节点前插入一个新的子节点
  1. 查找
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素的Name属性的值
getElementById() //通过元素Id,唯一性

数组对象有哪些原生方法?

pop、push、shift、unshift、splice、reverse、sort、concat、join、slice、toString、indexOf、lastIndexOf、reduce、reduceRight
forEach、map、filter、every、some

字符串常用的方法

  • 1. indexOf() 查找某字符串对应的下标,如果找不到返回-1
  • 2. split(sep) 将字符串按照指定的字符切割成数组元素,sep表示指定的字符
  • 3. slice(start,end) 截取字符串,start开始的下标,end结束的下标,不包含end本身;如果end为空截取到最后,如果为负数表示倒数。
  • 4. substr(start,count) 截取字符串,start开始的下标,count截取的长度,如果count为空截取到最后,如果start为负值表示倒数
  • 5. substring(start,end) 截取字符串,start开始的下标,end结束的下标,不包含end本身,如果end为空截取到最后;如果下标为负数,自动转为0

什么是Cookie隔离(请求资源的时候不带cookie要怎么做)

通过使用多个非主要域名来请求静态文件

  • 如果静态文件都放在主域名下,那静态文件请求的时候带有的cookie的数据提交给server(服务器)是非常浪费的,还不如隔离开。
  • 因为cookie有域的限制,因此不能跨域提交请求,故使用非主要域名的时候,请求头中就不会带有cookie数据,这样可以降低请求头的大小,降低请求时间,从而达到降低整体请求延时的目的。
  • 同时这种方式不会将cookie传入server,也减少了server对cookie的处理分析环节,提高了server的http请求的解析速度。

鼠标响应事件?

  • onclick鼠标点击某个对象;
  • onfocus获取焦点;
  • onblur失去焦点;
  • onmousedown鼠标被按下

flash和js通过什么类如何交互?

Flash提供了ExternalInterface接口与JavaScript通信
ExternalInterface有两个方法,call和addCallback
call的作用是让Flash调用js里的方法,addCallback是用来注册flash函数让js调用

Flash与Ajax各自的优缺点?

Flash

  • 优点:适合处理多媒体、矢量图形、访问机器。
  • 缺点:对css、处理文本不足,不容易被搜索。

Ajax

  • 优点:对css、文本支持很好,
  • 缺点:对多媒体、矢量图形、访问机器不足。

有效的javascript变量定义

第一个字符必须是一个字母、下划线”_”或一个美元符号($)
其他字符可以是字母、下划线、美元符号或数字

XML与JSON的区别

数据体积方面:JSON相对于XML来讲,数据的体积小,传递的速度更快些。

数据交互方面:JSON与JavaScript的交互更加方便,更容易解析处理,更好的数据交互。

数据描述方面:JSON对数据的描述性比XML较差。

传输速度方面:JSON的速度要远远快于XML。

HTML与XML的区别

  1. XML用来传输和存储数据,HTML用来显示数据
  2. XML使用的标签不用预先定义
  3. XML标签必须成对出现
  4. XML对大小写敏感
  5. XML中空格不会被删减
  6. XML中所有特殊符号必须用编码表示
  7. XML中的图片必须有文字说明

Web Worker和Web Socket

web socket:在一个单独的持久连接上提供全双工、双向的通信。使用自定义的协议(ws://、wss://)
同源策略对web socket不适用。

web worker:运行在后台的JavaScript,不影响页面的性能

var worker = new Worker(url); //创建worker
worker.postMessage(data); //向worker发送数据
worker.onmessage; //接收worker返回的数据
worker.terminate(); //终止一个worker的执行

JS垃圾回收机制?

  1. 标记清除
  • 这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。
  • 这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象。从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。
  1. 引用计数
  • 这是最简单的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。
  • 该算法有个限制:无法处理循环引用。两个对象被创建,并互相引用,形成了一个循环。它们被调用之后不会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收。

如何删除一个cookie?

  1. 将cookie的失效时间设置为过去的时间(expires)
document.cookie = "user="+ encodeURIComponent("name") + "expires="+ new Date(0);
  1. 将系统时间设置为当前时间往前一点时间
var date = new Date();
date.setDate(date.getDate()-1);

数组去除重复的方法有哪些?

  • 使用 set
function uniquearray(array) { 
 let unique_array= Array.from(set(array)) 
 return unique_array;
}
  • 使用 filter
function unique_array (arr) {
  let unique_array = arr.filter(function(elem, index, self) {
    return index == self.indexOf(elem);
  })
  return unique_array;
}

 console.log(unique_array(array_with_duplicates));
  • 使用 for 循环
let dups_names = ['Ron', 'Pal', 'Fred', 'Rongo', 'Ron'];
function dups_array(names) {
 let unique = {};
 names.forEach(function(i) {
    if (!unique[i]) {
      unique[i] = true;    }
  });
 return Object.keys(unique);
}   
console.log(dups_array(dups_names)) // Ron, Pal, Fred, Rongo
  • 使用reduce方法
// 暂时空着

JS中的substr()和substring()函数有什么区别

  • substr() 函数的形式为substr(startIndex,length) 它从startIndex返回子字符串并返回’length’个字符数
var s = "hello";
s.substr(1,4) == "ello"  // true
  • substring() 函数的形式为substring(startIndex,endIndex) 它返回从startIndex到endIndex – 1的子字符串
var s = "hello";
s.substring(1,4) == "ell"  // true

Set、Map和Array的异同、用法

  • Set:创建类似数组的数据结构,但成员是唯一(基本数据类型无重复,应用数据类型可以重复)
    常用方法:
  • size()、add()、delete()、clear()、has()可以使用该方法求交集/差集、keys()、values()、entries()、forEach()
  • Map:以键值对的形式存储数据,key可以是任何数据类型
    常用方法:
  • size()、set(key,value)、get(key)、delete()、clear()、has()、keys()、values()、entries()、forEach()

Set/Map转Array:

  • Array.from(Set)
  • const arr = [ …Set ]
  • const arr = [ …Map.values ]

类数组转成数组的方法

  • const arr = Array.from(args)
  • const arr = Array.prototype.slice.call(args)或者[].splice.call(args)
  • const arr = […args]

如何通过类别名获取dom元素

在 JS 中使用document.getElementsByClassName() 方法来获取具有类名的元素。

解释JS中的MUL函数

mul表示数的简单乘法.在这种技术中,将一个值作为参数传递给一个函数,而该函数将返回另一个函数,将第二个值传递给该函数,然后重复继续.例如:x(‘‘)y(‘‘)z可以表示为

function mul (x) {  
return function (y) { 
  return function (z) {   
   return x * y * z;
  }
 }
}

JS中如何将页面重定向到另一个页面?

  1. 使用location.href: window.location.href = ‘https://www.xxxx.com’
    调用此方法网页会被写入在浏览器历史记录中.
  2. 使用location.replace: window.location.replace(“https://www.xxxx.com”)
    将目前浏览器的地址替换掉,调用这个方法的网页,将不会被写入浏览记录。

列出JS中的一些设计模式:

设计模式是软件设计中常见问题的通用可重用解决方案,以下是一些设计模式:

  • 创建模式: 该模式抽象了对象实例化过程.
  • 结构型模式:这些模式处理不同的类和对象以提供新功能.
  • 行为模式:也称发布-订阅模式,定义了一个被观察者和多个观察者的,一对多的对象关系.
  • 并行设计模式: 这些模式处理多线程编程范例.
  • 架构设计模式: 这些模式用于处理架构设计.

如何在JS中动态添加/删除对象的属性?

可以使用object.property_name = value 向对象添加属性
delete object.property_name 用于删除属性

let user = new Object()
user.name = 'demo'
user.age = 25 
console.log(user) 
delete user.age 
console.log(user) 

JS中=&=和===区别是什么?

  • 对于string,number等基础类型,=&=和===有区别
  • 1. 不同类型间比较,==之比较“转化成同一类型后的值”看“值”是否相等,===如果类型不同,其结果就是不等。
  • 2. 同类型比较,直接进行“值”比较,两者结果一样。
  • 对于Array,Object等高级类型,=&=和===没有区别
  • 1. 进行“指针地址”比较。
  • 基础类型与高级类型,=&=和===有区别
  • 1. 对于==,将高级转化为基础类型,进行“值”比较。
  • 2. 因为类型不同,===结果为false。

JS中的匿名函数是什么?

  • 匿名函数:就是没有函数名的函数,如:
(function(x, y){
    alert(x + y);  
})(2, 3);
// 这里创建了一个匿名函数(在第一个括号内),第二个括号用于调用该匿名函数,并传入参数。

是否可以在JS中执行301重定向?

  • JS完全运行在客户端上。301是服务器作为响应发送的响应代码。因此,在JS中不可能执行301重定向。

解释JS中的事件冒泡和事件捕获

  • 事件捕获和冒泡: 在html DOM API中,有两种事件传播方法,它们决定了接收事件的顺序。两种方法是事件冒泡和事件捕获。第一个方法事件冒泡将事件指向其预期的目标,第二个方法称为事件捕获,其中事件向下到达元素。
  • 1. 事件捕获
    捕获过程很少被使用,但是当它被使用时,它被证明是非常有用的。这个过程也称为滴流模式。在这个过程中,事件首先由最外层的元素捕获,然后传播到最内部的元素。例如:
<div>
  <ul>
    <li></li>
  </ul>
</div>
// 从上面的示例中,假设单击事件发生在li元素中,在这种情况下,捕获事件将首先处理div,然后处理ul,最后命中目标元素li。
  • 2. 事件冒泡
    冒泡的工作原理与冒泡类似,事件由最内部的元素处理,然后传播到外部元素。
<div>
  <ul>
    <li></li>
  </ul>
</div>
// 从上面的例子中,假设click事件确实发生在冒泡模型中的li元素中,该事件将首先由li处理,然后由ul处理,最后由div元素处理。

如何将文件的所有导出作为一个对象?

  • import * as objectname from ‘./file.js’用于将所有导出的成员导入为对象。 可以使用对象的点(.)运算符来访问导出的变量或方法,如:
objectname.member1;
objectname.member2;
objectname.memberfunc();

module.exports 和 exports 之间有什么区别?

module和exports是Node.js给每个js文件内置的两个对象。可以通过console.log(module)和console.log(exports)打印出来。如果你在main.js中写入下面两行,然后运行 node main.js

console.log(exports);//输出:{}
console.log(module);//输出:Module {..., exports: {}, ...} (注:...代表省略了其他一些属性)
  • 从打印咱们可以看出,module.exports和exports一开始都是一个空对象{},实际上,这两个对象指向同一块内存。这也就是说module.exports和exports是等价的(有个前提:不去改变它们指向的内存地址)。
  • 例如:exports.age = 18和module.export.age = 18,这两种写法是一致的(都相当于给最初的空对象{}添加了一个属性,通过require得到的就是{age: 18})。

import和exports是什么?

import和exports 帮助咱们编写模块化的JS代码。使用import和exports,咱们可以将代码分割成多个文件。import只允许获取文件的某些特定变量或方法。可以导入模块导出的方法或变量。

 //person.js

 let name ='Sharad', occupation='developer', age =26;

 export { name, age}; 

  //index.js

 import name,age from './person'; 

 console.log(name);
 console.log(age);

JS中有哪些不同类型的弹出框可用

在JS中有三种类型的弹出框可用,分别是:

  • Alert
  • Confirm
  • Prompt

如何将JS日期转换为ISO标准

toISOString() 方法用于将js日期转换为ISO标准.它使用ISO标准将js Date对象转换为字符串。如:

var date = new Date();
var n = date.toISOString();
console.log(n);
// YYYY-MM-DDTHH:mm:ss.sssZ

如何在JS中编码和解码 URL

encodeURI() 函数用于在JS中对URL进行编码。它将url字符串作为参数并返回编码的字符串。
注意: encodeURI()不会编码类似这样字符:/ ? : @ & = + $ #,如果需要编码这些字符,请使用encodeURIComponent()。用法:

var uri = "my profile.php?name=sammer&occupation=pāntiNG";
var encoded_uri = encodeURI(uri);

decodeURI() 函数用于解码js中的URL。它将编码的url字符串作为参数并返回已解码的字符串,用法:

var uri = "my profile.php?name=sammer&occupation=pāntiNG";
var encoded_uri = encodeURI(uri);
decodeURI(encoded_uri);

BOM和DOM的关系

BOM全称Browser Object Model,即浏览器对象模型,主要处理浏览器窗口和框架。

DOM全称Document Object Model,即文档对象模型,是 HTML 和XML 的应用程序接口(API),遵循W3C 的标准,所有浏览器公共遵守的标准。

JS是通过访问BOM(Browser Object Model)对象来访问、控制、修改客户端(浏览器),由于BOM的window包含了document,window对象的属性和方法是直接可以使用而且被感知的,因此可以直接使用window对象的document属性,通过document属性就可以访问、检索、修改XHTML文档内容与结构。因为document对象又是DOM的根节点。

可以说,BOM包含了DOM(对象),浏览器提供出来给予访问的是BOM对象,从BOM对象再访问到DOM对象,从而js可以操作浏览器以及浏览器读取到的文档。

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容