技术实践

JavaScript知多少

在一些高频触发的事件中(如onresize,scroll,mousemove等),有时候我们并不希望每次触发事件就执行回调,这时候我们就需要

羊先生的头像
羊先生2020.03.16 · 6 分钟阅读 · 54 阅读
JavaScript知多少封面
文章正文还需 6 分钟

delete

delete只能删除属性,不能删除变量

用var创建的是变量不能被delete消除。而省略var时声明的是全局对像window的一个属性,可以被delete消除。

js 复制代码
a = 1;        //声明一个全局对像window的属性
var b = 2;    //创建一个全局变量
delete a;
delete b;
alert(a);     //error: a is not defined
alert(b)      //2   变量没有被删除
new
用new操作符创建对像时会改变默认的this,this将指向构造函数

function Fn(){
    alert(this);
}
Fn();            //[object Window]
new Fn();        //[object Object]

执行顺序

javascript是一门解释语言,默认情况下是从上到下顺序执行的。 但这也不是绝对的。在没有用关键字var申明的全局函数就会首先被执行(函数声明提升)

js 复制代码
aF();    //2
aV();    //error: bV is not defined 
function aF(){
    b(b);    //调用全局函数b
}
unction b(){
    alert(2);
}
function aV(){
    alert(bV);    //调用全局变量bV
}
bV = 'bv';
使用var声明的变量会被提到执行环境有最上方,但变量的赋值还是在原来的位置(变量声明提升)

if (true) {
    var a = 123;
}
alert(a);    //123   外面能够访问到if语句块里面声明的变量

实例属性会覆盖原型属性

javascript变量搜索是沿着作用域链反向查找的。因此,当在构造函数和原型中声明了相同的属性时,构造函数中的属性会优先于原型有的属性被查找,当变量搜索机制在构造函数中找到属性时,变量查询将会停止。

js 复制代码
function F(name){
    this.name = name;
}
F.prototype = {
    name: 'javascript:;'
}
var f = new F('Backbone');
console.log(f.name);    //Backbone

var fn = new F();
console.log(fn.name);    //undefined 即使没有向构造函数传值,由于构造函数已经声明了name属性,所以变量查询也不会继续在原型中查找。

for……in vs Object.keys()

for……in 通常用于遍历对象的key,但原型上的key也会被一并返回

Object.keys() 返回对象实例属性key组成的数组

js 复制代码
Object.prototype.a = 'aa';

let obj = {
ob: 'bb'
};

for(var i in obj) {
oconsole.log(i);
}
// 输出
// a
// b

console.log(Object.keys(obj)); // [ b ]

for……of vs forEach

for……of 通常用于遍历数组,返回数组value

forEach 也用于遍历数组,但在回调之内,用 break 不能中断循环,return 也不能返回到父级作用域

js 复制代码
et arr = [3, 5, 7];

// for……of
for(const i of arr) {
oconsole.log(i)
}

// forEach
arr.forEach((item, index) => {
oconsole.log(index + '-----' + item);
});
## 数组克隆
// es5
var a1 = [1,2];
var a2 = a1.concat();

// es6
var a1 = [1,2,3];
var a2 = [...a1];

对象克隆

通过JSON.stringify 和 JSON.parse 克隆
原理:通过JSON.stringify将对象转化成字符串,再将字符串赋值给其它变量,最后再用JSON.parse解析成对象

如果对象中包含函数,刚会被过滤掉。

js 复制代码
var obj = {a: 1};
var newObj = JSON.parse(JSON.stringify(obj));
newObj.a = 2;
console.log(obj); // { a: 1 }
console.log(newObj); // { a: 2 }

通过遍历对象的 key 克隆

js 复制代码
function clone(obj) {
    var that = this;
    var o;  
    if (obj.constructor == Object){  
        o = new obj.constructor();   
    }else{  
        o = new obj.constructor(obj.valueOf());   
    }  
    for(var key in obj){  
        if ( o[key] != obj[key] ){   
            if ( typeof(obj[key]) == 'object' ){   
                o[key] = that.clone(obj[key]);  
            }else{  
                o[key] = obj[key];  
            }  
        }  
    }  
    o.toString = obj.toString;  
    o.valueOf = obj.valueOf;  
    return o;
}
var obj = {a:1};
var newObj = clone(obj);
newObj.a = 22;
console.log(obj); // {a: 1}
console.log(newObj); // {a: 22, toString: ƒ, valueOf: ƒ}

函数节流

在一些高频触发的事件中(如onresize,scroll,mousemove等),有时候我们并不希望每次触发事件就执行回调,这时候我们就需要用到函数节流

以函数方式实现函数节流

js 复制代码
// 节流函数
function throttle(fn, interval = 500){
    let timer = null;
    return function(...args){
        let ctx = this;
        if(timer) return;
        timer = setTimeout(function(){
            clearTimeout(timer)
            timer = null;
            fn.apply(ctx, args)
        }, interval)
    }
}

// 绑定onresize
window.onresize = function(){
    console.log(Math.random() + '===========')
    t(fn,this,200);
}

函数防抖

函数防抖主要用于持续触发某个事件,但我们只希望最后一次触发才执行的回调方法的场景,比如搜索框内容变更时进行实时查询

js 复制代码
const debounce = function(fn, delay) {
    // 此处timer用到了闭包,防止timer被释放掉
    let timer = null;
    return function(...args){
        clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, args);
        }, delay)
    }
}

function test(){
    console.log(new Date())
}

document.addEventListener('scroll',debounce(test, 500))
Array.from()

将类数组(arguments)和可遍历的对象(Set、Map)转化为数组

js 复制代码
// arguments
function a(){
    console.log(Array.from(arguments)); 
}
a('a','ss','dd'); // ['a','ss','dd']

// Set
let set = new Set([1, 3, 3, 4, 4, 6]);
console.log(Array.from(set)); // [1, 3, 4, 6]
Array.keys()

返回一个新的Array迭代器,它包含数组中每个索引的键

js 复制代码
let arr = new Array(5);

console.log(Array.from(arr.keys())); // [0,1,2,3,4]
Array.of()

将一组值转化为数组

console.log(Array.of(1, 2, 3)); // [1,2,3]
Array 原型方法 find()
用法:arr.find(fun)

find方法返回第一个满足条件的元素

js 复制代码
let arr = [1,2,'a',4,'a',4];
console.log(arr.find(function(value){
    return value == 'a'
}));

Array 原型方法 findIndex()

用法:arr.findIndex(fun)

findIndex方法返回第一个满足条件的元素的索引值

js 复制代码
let arr = [1,2,'a',4,'a',4];
console.log(arr.findIndex(function(value){
    return value == 'a'
}));

Array 原型方法 reduce()
用法:arr.reduce(fun)

findIndex返回数组每一项相加的和

js 复制代码
let arr = [1,2,3,4];
let total = arr.reduce(function(a,b){
    return a+b;
});
console.log(total);

Array 原型方法 includes()

es7
用法:arr.includes(value)

includes方法用于检测数组中是否包含某一个值

js 复制代码
let arr = [1,2,'a',4,'a',4];
console.log(arr.includes('a'));

Array 原型方法 indexOf()

用法:arr.indexOf(value)

indexOf方法用于返回value在当前数组中第一次出现的位置,如果没有则返回-1。

js 复制代码
let arr = [3, 4, 5, 3];
console.log(arr.indexOf(3));

Array 原型方法 fill()

用法:arr.fill(value)

fill 方法使用给定值,填充一个数组。

js 复制代码
let arr = new Array(4);
arr.fill(5);
console.log(arr); //[ 5, 5, 5, 5 ]
Object.keys()
es8
用法:Object.keys(obj);

返回obj的索引值组成的数组

js 复制代码
let obj = {
    a: 'aaa',
    b: 'bbb',
    c: 'ccc'
};
console.log(Object.keys(obj)); // [ 'a', 'b', 'c' ]
Object.values()

用法:Object.values(obj);

返回obj的value组成的数组

js 复制代码
let obj = {
    a: 'aaa',
    b: 'bbb',
    c: 'ccc'
};
console.log(Object.values(obj)); // [ 'aaa', 'bbb', 'ccc' ]
Object.assign()
用法:Object.assign(obj[,obj]);

把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

js 复制代码
llet obj1 = {
    a: 1
};
let obj2 = Object.assign({}, obj1);

console.log(obj2); // { a: 1 }

obj2.a = 2;

console.log(obj1); // { a: 1 }

console.log(obj2); // { a: 2 }
console.log(Object.values(obj)); // [ 'aaa', 'bbb', 'ccc' ]