JavaScript小记(持续更新)

学习js遇到的疑问和js基础都记录在这里,持续更新中。javascript

JavaScript小记(持续更新)

一、正则表达式

/b+/g 至少出现一次b(1~n次)php

/b*/g 能够不出现b,也能够出现一次或屡次(0~n次)css

/b{n,m}/g 最少出现n次b,最多出现m次b(n~m次)html

/colou?r/g 能够匹配color或colour,?表示前面的字符最多只出现一次(0次或1次)前端

二、代码回收规则

1.全局变量不会被回收java

2.局部变量会被回收,也就是函数一旦运行完之后,函数内部的东西都会被销毁node

3.只要被另一个做用域所引用就不会被回收jquery

三、数据类型

基本数据类型:undefined、null、string、number、boolean、symbolios

复杂数据类型:objectes6

引用类型:array、function、object

四、JS中数据类型的判断( typeof,instanceof,constructor,Object.prototype.toString.call() )

  • typeof

    对一个值使用typeof操做符可能返回:

    undefined、string、number、boolean、symbol、object(对象或null)、function

    console.log(typeof 2);               // number
    console.log(typeof true);            // boolean
    console.log(typeof 'str');           // string
    console.log(typeof []);              // object  []数组的数据类型在 typeof 中被解释为object
    console.log(typeof function(){});    // function
    console.log(typeof {});              // object
    console.log(typeof undefined);       // undefined
    console.log(typeof null);            // object    null 的数据类型被 typeof 解释为 object
  • instanceof

    只有引用数据类型(Array,Function,Object)被精准判断,其余(数值Number,布尔值Boolean,字符串String)字面值不能被instanceof精准判断。

    console.log(2 instanceof Number);                    // false
    console.log(true instanceof Boolean);                // false 
    console.log('str' instanceof String);                // false  
    console.log([] instanceof Array);                    // true
    console.log(function(){} instanceof Function);       // true
    console.log({} instanceof Object);                   // true    
    // console.log(undefined instanceof Undefined);
    // console.log(null instanceof Null);
  • constructor

    console.log((2).constructor === Number);                  // true
    console.log((true).constructor === Boolean);              // true
    console.log(('str').constructor === String);             // true
    console.log(([]).constructor === Array);                  // true
    console.log((function() {}).constructor === Function);  // true
    console.log(({}).constructor === Object);               // true

    用costructor来判断类型看起来是完美的,然而,若是我建立一个对象,更改它的原型,这种方式也变得不可靠了。

    function Fn(){};
    
    Fn.prototype = new Array();
    
    var f = new Fn();
    
    console.log(f.constructor === Fn);    // false
    console.log(f.constructor === Array); // true
  • object.prototype.toString.call()

    console.log(Object.prototype.toString.call(2));                // [object Number]
    console.log(Object.prototype.toString.call(true));            // [object Boolean]
    console.log(Object.prototype.toString.call('str'));            // [object String]
    console.log(Object.prototype.toString.call([]));            // [object Array]
    console.log(Object.prototype.toString.call(function(){}));    // [object Function]
    console.log(Object.prototype.toString.call({}));            // [object Object]
    console.log(Object.prototype.toString.call(undefined));        // [object Undefined]
    console.log(Object.prototype.toString.call(null));            // [object Null]

​ 使用 Object 对象的原型方法 toString ,使用 call 进行狸猫换太子,借用Object的 toString 方法结果精准的显示咱们须要的数据类型。就算咱们改变对象的原型,依然会显示正确的数据类型。

五、JavaScript实现继承共六种方式

详细请看这篇文章:https://www.cnblogs.com/humin...

  • 原型链继承:利用原型让一个引用类型继承另一个引用类型的属性和方法。
  • 构造函数继承:在子类型构造函数的内部调用超类构造函数,经过使用call()和apply()方法能够在新建立的对象上执行构造函数。
  • 组合继承:将原型链和借用构造函数的技术组合在一块,从而发挥二者之长的一种继承模式。
  • 原型式继承:借助原型能够基于已有的对象建立新对象,同时还没必要须所以建立自定义的类型。
  • 寄生式继承:建立一个仅用于封装继承过程的函数,该函数在内部以某种方式来加强对象,最后再像真正是它作了全部工做同样返回对象。
  • 寄生组合式继承:经过借用函数来继承属性,经过原型链的混成形式来继承方法。

六、改变做用域链的方法

with、try...catch、eval

七、Ajax

Ajax技术核心就是XMLHttpRequest对象。

(1)设置请求参数(请求方式,请求页面的相对路径,是否异步)

(2)设置回调函数,一个处理服务器响应的函数,使用 onreadystatechange ,相似函数指针

(3)获取异步对象的readyState 属性:该属性存有服务器响应的状态信息。每当 readyState 改变时,onreadystatechange 函数就会被执行。

(4)判断响应报文的状态,若为200说明服务器正常运行并返回响应数据。

(5)读取响应数据,能够经过 responseText 属性来取回由服务器返回的数据。

var xhr = new XMLHttpRequest();                // 建立Ajax对象
xhr.open('get', 'aabb.php', true);            // xhr发送请求
xhr.send(null);
xhr.onreadystatechange = function() {         // xhr获取响应
    if(xhr.readyState == 4) {
        if(xhr.status == 200) {
            console.log(xhr.responseText);
        }
    }
}
 /* ajax返回的状态:
     0:(未初始化)请求尚未创建(open执行前) 
     1:(载入)请求创建了还没发送(执行了open) 
     2:(载入完成)请求正式发送,send()方法执行完成,已经接收到所有响应内容(执行了send) 
     3:(交互)请求已受理,有部分数据能够用,但尚未处理完成,正在解析响应内容
     4:(完成)响应内容解析完成,能够在客户端调用了 
*/

八、Ajax,jQuery ajax,axios和fetch的区别

Ajax:

​ ajax,最先出现的发送后端请求技术,隶属于原始js中,核心使用XMLHttpRequest对象,多个请求之间若是有前后关系的话,就会出现回调地狱。

Jquery Ajax:

是jQuery框架中的发送后端请求技术,因为jQuery是基于原始的基础上作的封装,因此,jquery Ajax天然也是对原始ajax的封装

$.ajax({
   type: 'POST',
   url: url,
   data: data,
   dataType: dataType,
   success: function () {},
   error: function () {}
});

​ 优缺点:

  • 自己是针对MVC的编程,不符合如今前端MVVM的浪潮
  • 基于原生的XHR开发,XHR自己的架构不清晰,已经有了fetch的替代方案
  • JQuery整个项目太大,单纯使用ajax却要引入整个JQuery很是的不合理(采起个性化打包的方案又不能享受CDN服务)

Fetch:

​ fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多了,参数有点像jQuery ajax。

​ 可是,必定记住fetch不是ajax的进一步封装,而是原生js。Fetch函数就是原生js,没有使用XMLHttpRequest对象。

try {
  let response = await fetch(url);
  let data = response.json();
  console.log(data);
} catch(e) {
  console.log("Oops, error", e);
}

axios:

​ axios不是原生JS的,须要进行安装,它不但能够在客户端使用,并且能够在nodejs端使用。Axios也能够在请求和响应阶段进行拦截。一样也是基于promise对象的。

https://blog.csdn.net/Roselan...

axios({
    method: 'post',
    url: '/user/12345',
    data: {
        firstName: 'Fred',
        lastName: 'Flintstone'
    }
})
.then(function (response) {
    console.log(response);
})
.catch(function (error) {
    console.log(error);
});

优缺点:

  • 从 node.js 建立 http 请求
  • 支持 Promise API
  • 客户端支持防止CSRF
  • 提供了一些并发请求的接口(重要,方便了不少的操做)

九、replace(/s/g,"")

"/ /"这个是固定写法,"s"是转移符号用以匹配任何空白字符,包括空格、制表符、换页符等等,"g"表示全局匹配将替换全部匹配的子串,若是不加"g"当匹配到第一个后就结束了。

这个例子的意思就是将原字符串中的全部空白字符替换成"",好比"abc d efg "字样的字符串使用这个函数后将变成"abcdefg"

通常用来把字符串中全部的空格去掉

十、Map和ForEach的区别

  • forEach(): 数组中的每一个元素执行一次回调函数
  • map(): 返回一个由原数组中的每一个元素调用一个指定方法后的返回值组成的新数组

示例

下方提供了一个数组,若是咱们想将其中的每个元素翻倍,咱们可使用mapforEach来达到目的。

let arr = [1, 2, 3, 4, 5];

ForEach

注意,forEach是不会返回有意义的值的。
咱们在回调函数中直接修改arr的值。

arr.forEach((num, index) => {
    return arr[index] = num * 2;
});

Map

let doubled = arr.map(num => {
    return num * 2;
});

十一、indexOf与search的区别

  • indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置,若是没有找到返回-1

    • 该方法将从头至尾地检索字符串stringObject,看它是否含有子串searchvalue。开始检索的位置在字符串的fromindex处。若是没有fromindex参数则从字符串的开头检索。若是找到一个searchvalue,则返回searchvalue的第一次出现的位置。stringObjec中的字符串位置是从0开始的。
    • indexOf()方法对大小写敏感。
  • search方法用于检索字符串中指定的子字符串,检索与正则表达式相匹配的子字符串。若是没有找到,返回-1。

    • search() 方法不执行全局匹配,它将忽略标志 g。它同时忽略 regexp 的 lastIndex 属性,而且老是从字符串的开始进行检索,这意味着它老是返回 stringObject 的第一个匹配的位置。
    • search() 方法对大小写敏感。

indexOf与search的区别

search()的参数必须是正则表达式,而indexOf()的参数只是普通的字符串。indexOf()是比search()更加底层的方法。

若是只是对一个具体字符串来检索,那么使用indexOf()的系统资源消耗更小,效率更高;若是查找具备某些特征的字符串(例如查找以a开头,后面是数字的字符串),那么indexOf()就无能为力,必需要使用正则表达式和search()方法了。

大可能是时候用indexOf()不是为了真的想知道子字符串的位置,而是想知道长字符串中有没有包含这个子字符串。r若是返回索引为-1,那么说明没有,反之则有。

十二、String对象中slice()、substring()、substr()的用法与区别

  • slice() 方法可提取字符串的某个部分,并以新的字符串返回被提取的部分。

    • 语法:stringObject.slice(start,end)
    • 返回一个新的字符串。包括字符串 stringObject 从 start 开始(包括 start)到 end 结束(不包括 end)为止的全部字符。
    • 若是是负数,则该参数规定的是从字符串的尾部开始算起的位置。也就是说,-1 指字符串的最后一个字符,-2 指倒数第二个字符,以此类推。
  • substring() 方法用于提取字符串中介于两个指定下标之间的字符。

    • 语法:stringObject.substring(start,stop)
    • 一个新的字符串,该字符串值包含 stringObject 的一个子字符串,其内容是从 start 处到 stop-1 处的全部字符,其长度为 stop减 start。
    • 若是 start 比 stop 大,那么该方法在提取子串以前会先交换这两个参数。
    • 与 slice() 和 substr() 方法不一样的是,substring() 不接受负的参数。
  • substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符。

    • 语法:stringObject.substr(start,length)
    • 一个新的字符串,包含从 stringObject 的 start(包括 start 所指的字符) 处开始的 length 个字符。若是没有指定 length,那么返回的字符串包含从 start 到 stringObject 的结尾的字符。
    • 若是是负数,那么该参数声明从字符串的尾部开始算起的位置。也就是说,-1 指字符串中最后一个字符,-2 指倒数第二个字符,以此类推。
    • ECMAscript 没有对该方法进行标准化,所以反对使用它。

String 对象的方法 slice()、substring() 和 substr() (不建议使用)均可返回字符串的指定部分。slice() 比 substring() 要灵活一些,由于它容许使用负数做为参数。slice() 与 substr() 有所不一样,由于它用两个字符的位置来指定子串,而 substr() 则用字符位置和长度来指定子串。

还要注意的是,String.slice() 与 Array.slice() 类似。

1三、Array对象中slice() 、splice()的区别

slice() 方法可从已有的数组中返回选定的元素。

  • 语法:arrayObject.slice(start,end)
  • 返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。在只有一个参数的状况下,返回从该参数指定位置开始到当前数组末尾的全部项。
  • 该方法不改变原字符串,而是返回新的字符串。若是想删除数组中的一段元素,应该使用方法 Array.splice()。
  • 若是slice()方法的参数中有一个负数,则用数组长度加上该数来肯定相应的位置。
  • 若是结束为止小于起始位置,则返回空数组。

splice() 方法主要用途是向数组的中部插入项。

  • 语法:arrayObject.splice(index,howmany,item1,.....,itemX)
  • index 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
    howmany 必需。要删除的项目数量。若是设置为 0,则不会删除项目。
    item1, ..., itemX 可选。向数组添加的新项目。
  • splice()方法始终都会返回一个数组,该数组中包含从原始数组中删除的项(若是没有删除任何项,则返回一个空数组)

1四、Object.assign()

语法:Obejct.assign(target,...sources)

用途:未来自一个或多个源对象中的值复制到一个目标对象,它将返回目标对象。其中对象的继承属性和不可枚举属性是不能拷贝的,因此不能使用它来实现深拷贝。

第一级属性深拷贝,从第二级属性开始就是浅拷贝。

若是多个源对象具备同名属性,则排位靠后的源对象会覆盖排位靠前的。但null或undefined被视为空对象同样对待,不会覆盖。

var receiver = {};
Object.assign(receiver, 
    {
        type: "js", 
        name:"file.js"
    },
    {
        type: "css"
    }
)
console.log(receiver); // {type: "css", name: "file.js"}
//实现 deepAssign({a: {b: 1, c: 2}}, {a: {c: 3}});
//=> {a: {b: 1, c: 3}}

// 只有两个对象
function deepAssign(obj1, obj2){
    for(var item in obj2){
        obj1[item] = typeof obj2[item] === 'object' ? deepClone(obj1[item], obj2[item]) : obj2[item];
    }
    return obj1;
}
// 通用
function deepAssign() {
  var args = Array.from(arguments);
  return args.reduce(deepClone, args[0]);

  function deepClone(target, obj){
    if(!target) target = Array.isArray(obj) ? [] : {};
      for(key in obj){
        target[key] = typeof obj[key] ==="object" ? deepClone(target[key], obj[key]) : obj[key]
      }
    return target;
  }
}

1五、ES6之Array.from()方法

一、类数组对象:所谓类数组对象,最基本的要求就是具备length属性的对象

二、Array.from()方法就是将一个类数组对象或者可遍历对象转换成一个真正的数组。

​ Array.from有三个参数,Array.from(arrayLike[, mapFn[, thisArg]]),

​ arrayLike:想要转换成数组的伪数组对象或可迭代对象;

​ mapFn:若是指定了该参数,新数组中的每一个元素会执行该回调函数;

​ thisArg:可选参数,执行回调函数 mapFn 时 this 对象。

​ 该方法的返回值是一个新的数组实例(真正的数组)。

例1:Array.from ({length:n}, Fn) 将类数组转换为数组

Array.from({length:3}, () => 'jack') //["jack", "jack", "jack"]
 
Array.from({length:3}, item => (item = {'name':'shao','age':18})) //[{'name':'shao','age':18}, {'name':'shao','age':18}, {'name':'shao','age':18}]
 
Array.from({length:2}, (v, i) => item = {index:i});
//生成一个index从0到4的数组对象[{index: 0},{index: 1}]
let array = {
    0: 'name', 
    1: 'age',
    2: 'sex',
    3: ['user1','user2','user3'],
    'length': 4
}
let arr = Array.from(array)
console.log(arr) // ['name','age','sex',['user1','user2','user3']]

若是将上面代码中length属性去掉arr将会是一个长度为0的空数组,若是将代码修改一下,就是具备length属性,可是对象的属性名再也不是数字类型的,而是其余字符串型的,代码以下:

let array = {
    'name': 'name', 
    'age': 'age',
    'sex': 'sex',
    'user': ['user1','user2','user3'],
    'length': 4
}
let arr = Array.from(array )
console.log(arr)  // [ undefined, undefined, undefined, undefined ]

会发现结果是长度为4,元素均为undefined的数组,因而可知,要将一个类数组对象转换为一个真正的数组,必须具有如下条件:
(1)该类数组对象必须具备length属性,用于指定数组的长度。若是没有length属性,那么转换后的数组是一个空数组。
(2)该类数组对象的属性名必须为数值型或字符串型的数字
该类数组对象的属性名能够加引号,也能够不加引号

例2:Array.from (obj, mapFn)

obj指的是数组对象、相似数组对象或者是set对象,map指的是对数组中的元素进行处理的方法。

let arr = [1,1,2,5,5,6,7,8,9]
let set = new Set(arr)
console.log(Array.from(set))  // [1, 2, 5, 6, 7, 8, 9]
//将数组中布尔值为false的成员指为0
Array.from([1, ,2,3,3], x => x || 0) //[1,0,2,3,3]
 
//将一个相似数组的对象转为一个数组,并在原来的基础上乘以2倍
let arrayLike = { '0': '2', '1': '4', '2': '5', length: 3 }
Array.from(arrayLike, x => x*2) //[4,8,10]
 
//将一个set对象转为数组,并在原来的基础上乘以2倍
Array.from(new Set([1,2,3,4]), x => x*2) //[2,4,6,8]

例3:Array.from(string) 将字符串转换为数组

Array.from('abc') //['a','b','c']

例子4——将Map解构转为数组,最方便的作法就是使用扩展运算符(...)

const myMap = new Map().set(true, 7)
console.log(myMap); //Map(1) {true => 7}
console.log([...myMap]); //[true ,7]

1六、数组去重的方法

一、利用ES6 Set去重(ES6中最经常使用)

function unique (arr) {
  return Array.from(new Set(arr))
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
 //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

不考虑兼容性,这种去重的方法代码最少。这种方法没法去掉“{}”空对象。

二、利用for嵌套for,而后splice去重(ES5中最经常使用)

function unique(arr){            
    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--;
            }
        }
    }
    return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]     
    //NaN和{}没有去重,两个null直接消失了

三、for...of + includes()

双重for循环的升级版,外层用 for...of 语句替换 for 循环,把内层循环改成 includes()

先建立一个空数组,当 includes() 返回 false 的时候,就将该元素 push 到空数组中

相似的,还能够用 indexOf() 来替代 includes()

function unique(arr) {
    var result = []
    for (var i of arr) {
        !result.includes(i) && result.push(i)
    }
    return result
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
   // [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}]      //NaN、{}没有去重

四、利用indexOf去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!');
        return;
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {
        if (array.indexOf(arr[i]) === -1) {
            array.push(arr[i]);
        }
    }
    return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
   // [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}]      //NaN、{}没有去重

五、利用sort()

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return;
    }
    arr = arr.sort()
    var arrry= [arr[0]];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            arrry.push(arr[i]);
        }
    }
    return arrry;
}
     var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
        console.log(unique(arr))
// [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined]      // NaN、{}没有去重

6 、利用hasOwnProperty

function unique(arr) {
    var obj = {};
    return arr.filter(function(item, index, arr){
        return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
    })
}
    var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}]   //全部的都去重了

七、利用filter

function unique(arr) {
  return arr.filter(function(item, index, arr) {
    //当前元素,在原始数组中的第一个索引==当前索引值,不然返回当前元素
    return arr.indexOf(item, 0) === index;
  });
}
    var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
        console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]

八、利用Map数据结构去重

function arrayNonRepeatfy(arr) {
  let map = new Map();
  let array = new Array();  // 数组用于返回结果
  for (let i = 0; i < arr.length; i++) {
    if(map.has(arr[i])) {  // 若是有该key值
      map.set(arr[i], true); 
    } else { 
      map.set(arr[i], false);   // 若是没有该key值
      array.push(arr[i]);
    }
  } 
  return array;
}
 var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
 console.log(arrayNonRepeatfy(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]
// {}没去

建立一个空Map数据结构,遍历须要去重的数组,把数组的每个元素做为key存到Map中。因为Map中不会出现相同的key值,因此最终获得的就是去重后的结果。

九、利用reduce+includes

function unique(arr){
    return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr));
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]

十、for...of + Object

首先建立一个空对象,而后用 for 循环遍历

利用对象的属性不会重复这一特性,校验数组元素是否重复

function unique(arr){
    let result = []
    let obj = {}
    for (let i of arr) {
        if (!obj[i]) {
            result.push(i)
            obj[i] = 1
        }
    }
    return result
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr));
// [1, "true", 15, false, undefined, null, NaN, 0, "a", {…}]

1七、对象数组去重

const objArr = [{
    name: '名称1'
},{
    name: '名称2'
},{
    name: '名称3'
},{
    name: '名称1'
},{
    name: '名称2'
}]

const obj = {}
const newObjArr = []
for(let i = 0; i < objArr.length; i++){
    if(!obj[objArr[i].name]){
        newObjArr.push(objArr[i]);
        obj[objArr[i].name] = true
    }
}

console.log(newObjArr);
/*结果:
     [{
        name: '名称1'
      },{
        name: '名称2'
      },{
        name: '名称3'
      }]
*/
const objArr = [{
    name: '名称1'
},{
    name: '名称2'
},{
    name: '名称3'
},{
    name: '名称1'
},{
    name: '名称2'
}]
const obj = {}
const newObjArr =  objArr.reduce((prev, curr)=>{
    obj[curr.name] ? true : obj[curr.name] = true && prev.push(curr);
    return prev
}, [])
console.log(newObjArr)

1八、fetch发送post请求时,老是发送两次,第一次状态码204,第二次才成功

由于你用的fetch post修改了请求头,致使fetch第一次发送一个options请求,询问服务器是否支持修改的请求头,如过服务器支持,那么将会再次发送真正的请求。

1九、跨域(jsonp,ajax)

什么是跨域

  • 浏览器有同源策略,不容许ajax访问其余域接口。
  • 跨域条件:协议、域名、端口有一个不一样就算跨域。

JSONP:ajax请求受同源策略影响,不容许进行跨域请求,而script标签src属性中的连接却能够访问跨域的js脚本,利用这个特性,服务端再也不返回JSON格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样实现了跨域。

JSONP的缺点:

JSON只支持get,由于script标签只能使用get请求;

JSONP须要后端配合返回指定格式的数据。

ajax与jsonp的区别

  • ajax和jsonp这两种技术再调用方式上“看起来很像”,目的也同样,都是请求一个url,而后把服务器返回的数据进行处理,所以jquery和ext等框架都把jsonp做为ajax的一种形式进行了封装。
  • 但ajax和jsonp在本质上是不一样的东西。ajax的核心是经过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加。

如何实现跨域

JSONP:经过动态建立script,再请求一个带参网址实现跨域通讯。

document.domain + iframe跨域:两个页面都经过js强制设置document.domain为基础主域,就实现了同域。

location.hash + iframe跨域:a欲与b跨域相互通讯,经过中间页c来实现。 三个页面,不一样域之间利用iframe的location.hash传值,相同域之间直接js访问来通讯。

window.name + iframe跨域:经过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。

postMessage跨域:能够跨域操做的window属性之一。

CORS:服务端设置Access-Control-Allow-Origin便可,前端无须设置,若要带cookie请求,先后端都须要设置。

代理跨域:起一个代理服务器,实现数据的转发

20、优化SPA应用的首屏加载速度慢的问题

  • 将公用的JS库经过script标签外部引入,减少 app.bundle 的大小,让浏览器并行下载资源文件,提升下载速度;
  • 在配置路由时,页面和组件使用懒加载的方式引入,进一步缩小 app.bundle 的体积,在调用某个组件时再加载对应的js文件;
  • 加一个首屏loading图,提高用户体验;

2一、jQuery获取的dom对象和原生的dom对象的区别

js原生获取的dom是一个对象,jQuery对象就是一个数组对象,其实就是选择出来的元素的数组集合,因此说他们二者是不一样的对象类型不等价。

原生DOM对象转jQuery对象:

var box = document.getElementById('box');
var $box = $(box);

jQuery对象转原生DOM对象:

var $box = $('#box');
var box = $box[0];

2二、深拷贝和浅拷贝

区别

1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用

2.深拷贝: 建立一个新的对象和数组,将原对象的各项属性的“”(数组的全部元素)拷贝过来,是“值”而不是“引用”

为何要使用深拷贝?

咱们但愿在改变新的数组(对象)的时候,不改变原数组(对象)

深拷贝数组

1.直接遍历

function copyArr(arr) {
    let res = []
    for (let i = 0; i < arr.length; i++) {
     res.push(arr[i])
    }
    return res
}
var arr = [1,2,3,4,5]
var arr2 = copyArr(arr)

2.slice()

var arr = [1,2,3,4,5]
var arr2 = arr.slice()

3.concat()

var arr = [1,2,3,4,5]
var arr2 = arr.concat()

深拷贝对象

1.迭代递归法

这是最常规的方法,思想很简单:就是对对象进行迭代操做,对它的每一个值进行递归深拷贝。

function deepClone(obj){
    var newObj= obj instanceof Array ? []:{};
    for(var item in obj){
        var temple= typeof obj[item] == 'object' ? deepClone(obj[item]):obj[item];
        newObj[item] = temple;
    }
    return newObj;
}

2.ES6的Object.assign(除了根属性是深拷贝,其他都是浅拷贝)

3.ES6扩展运算符:...

“一招鲜,吃遍天” ——序列化反序列化法

JSON.parse(JSON.stringify(XXXX))

var array = [
    { number: 1 },
    { number: 2 },
    { number: 3 }
];
var copyArray = JSON.parse(JSON.stringify(array))
copyArray[0].number = 100;
console.log(array); //  [{number: 1}, { number: 2 }, { number: 3 }]
console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

它也只能深拷贝对象和数组,对于其余种类的对象,会失真。这种方法比较适合日常开发中使用,由于一般不须要考虑对象和数组以外的类型。

2三、清空数组的方法

方式1:splice函数

arrayObject.splice(index,howmany,element1,.....,elementX)

index:必选,规定从何处添加/删除元素。

howmany:必选,规定应该删除多少元素。未规定此参数,则删除从 index 开始到原数组结尾的全部元素。

element1:可选,规定要添加到数组的新元素。

<script type ="text/javascript">  
  var arr = [1,2,3,4];  
   arr.splice(0,arr.length);  
</script>

方式2:给数组的length赋值为0

<script type ="text/javascript">  
  var arr = [1,2,3,4];  
   arr.length = 0;
</script>

赋予数组的长度小于自己的长度,数组中后面的元素将被截断。

赋予数组的长度大于自己的长度,将扩展数组长度,多的元素为undefined。

方式3:直接赋予新数组 []

<script type ="text/javascript">  
  var arr = [1,2,3,4];  
   arr = [];
</script>

这种方式为将arr从新复制为空数组,以前的数组若是没有被引用,将等待垃圾回收。

2四、Pomise.all的使用

所有完成以后统一执行

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})

let p2 = new Promise((resolve, reject) => {
  resolve('success')
})

let p3 = Promise.reject('失败')

Promise.all([p1, p2]).then((result) => {
  console.log(result)               //['成功了', 'success']
}).catch((error) => {
  console.log(error)
})

Promise.all([p1,p2,p3]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)      // 失败了,打出 '失败'
})
let wake = (time) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${time / 1000}秒后醒来`)
    }, time)
  })
}

let p1 = wake(3000)
let p2 = wake(2000)

Promise.all([p1, p2]).then((result) => {
  console.log(result)       // [ '3秒后醒来', '2秒后醒来' ]
}).catch((error) => {
  console.log(error)
})

须要特别注意的是,Promise.all得到的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即使p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程当中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问能够解决这个问题。

2五、简单实现 Promise.all

function promiseAll(promises) {
    return new Promise((resolve, reject) => {
      let resultCount = 0;
      let results = new Array(promises.length);

      for (let i = 0; i < promises.length; i++) {
        promises[i].then(value => {
          resultCount++;
          results[i] = value;
          if (resultCount === promises.length) {
            return resolve(results)
          }
        }, error => {
          reject(error)
        })
      }
    })
  }

  let p1 = new Promise(resolve => resolve('p1'))
  let p2 = new Promise(resolve => resolve('p2'))
  let p3 = Promise.reject('p3 error')

  promiseAll([p1, p2]).then(results => {
    console.log(results)    // ['p1', 'p2']
  }).catch(error => {
    console.log(error)
  })

  promiseAll([p1, p2, p3]).then(results => {
    console.log(results)
  }).catch(error => {
    console.log(error)      // 'p3 error'
  })

2六、Promise.race的使用

只要有一个完成就执行,返回为最早完成的

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  },1000)
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('failed')
  }, 500)
})

Promise.race([p1, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)  // 打开的是 'failed'
})

2七、做用域链

var a = 100
function F1() {
    var b = 200
    function F2() {
        var c = 300
        console.log(a)    // a是自由变量,向父级做用域寻找a,未果,再向上一级父级做用域寻找a
        console.log(a)    // b是自由变量,向父级做用域寻找b
        console.log(a)
    }
    F2()
}
F1()               // 自由变量不断地往父级做用域去找,造成一个链式结构

2八、 一个完整的JavaScript实现应该由下列三个不一样的部分组成

1.核心(ECMAScript) ——提供核心语言功能
2.文档对象模型(Dom) ——提供访问和操做网页内容的方法和接口
3.浏览器对象模型(Bom) ——提供与浏览器交互的方法和接口

2九、延迟脚本

defer=“defer”
defer属性:代表脚本在执行时不会影响页面的构造,也就是说,脚本会被延迟到整个页面都解析完毕再运行

30、NaN

var result = “a” < 3; // false,由于“a”被转化成了NaN
根据规则,任何操做数与NaN进行关系比较,结果都是false

3一、在函数体内能够经过arguments对象来访问这个参数数组,从而获取传递给函数的每个参数

function doadd() {
    if (arguments.length == 1) {
        alert(arguments[0] + 10);
    } else if (arguments.length == 2) {
        alert(arguments[0] + arguments[1]);
    }
}
doAdd(10) // 20
doAdd(30, 20) // 50

3二、js数组拍平

一、递归

function flatArr(arr) {
    var newArr = [];
    arr.forEach((val) => {
        if (Array.isArray(val)) {
            newArr = newArr.concat(flatArr(val))
        } else {
            newArr.push(val)
        }
    })
    return newArr;
}

二、reduce

function flatArr(arr) {
   return arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) ? flatArr(cur) : cur), [])
}

3.利用数组join()或toString()方法

// join()
function flatArr(arr) {
      arr.join().split(',')
    return JSON.parse(`[${arr.join()}]`)
}
// toString()
function flatArr(arr) {
    arr.toString().split(',')
    return JSON.parse(`[${arr.toString()}]`)
}

4.es6数组的flat()方法 (浏览器版本太低不支持)

flat方法默认打平一层嵌套,也能够接受一个参数表示打平的层数,传 Infinity 能够打平任意层。

function flatArr(arr) {
      return arr.flat(Infinity);
}

3三、JavaScript 利用 async await 实现 sleep 效果

const sleep = (timeountMS) => new Promise((resolve) => {
  setTimeout(resolve, timeountMS);
});

(async () => {
  console.log('11111111, ' + new Date());
  await sleep(2000);
  console.log('22222222, ' + new Date());
  await sleep(2000);
  console.log('33333333, ' + new Date());
})();

3四、手写设置cookie

1.设置cookie一天后过时

function setCookie(name,expireday){
    var dayobject = new Date();  // Date()函数获取当前的日期和时间
    // getTime()函数获取的事1970年1月1号至今的毫秒数
    // 注意要多加8小时,咱们位于东八区比标准时间相差8小时
    var daynum = dayobject.getTime() + expireday*(24+8)*60*60*1000;
    // 计算过时时间毫秒数
    dayobject.setTime(daynum);
    // 设置超时的时间
    alert('name=' + name + ';' + 'expires=' + dayobject.toGMTString());
    document.cookie = 'name=' + name + ';' + 'expires=' + dayobject.toGMTString();
}
setCookie('coco',1)

2.设置cookie立刻过时

function delCookie(name){
    var expires = new Date();
    expires.setTime(expires.getTime() - 1);
    document.cookie = 'name='+name+';'+'expires=' + expires.toGMTString();
}
// 设置cookie的过时时间是比当前时间提早一秒,也就是立马过时了。
相关文章
相关标签/搜索