数据类型

一般认为js数据类型为7种:number,string,boolean,undefined,null,symbol,object,(有的把bigint也算一种,)其中object为引用类型,其余为基本类型。

console.log(typeof 2); // number
console.log(typeof true); // boolean
console.log(typeof "str"); // string
console.log(typeof []); // object
console.log(typeof {}); // object
console.log(typeof function () {}); // function
console.log(typeof undefined); // undefined
console.log(typeof null); // object

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({}.constructor === Object); // true
console.log(function () {}.constructor === Function); // true

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

instanceof

instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置,不能用来判断包装对象。

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(Object instanceof Object); //true
console.log(Function instanceof Object); //true

// 原理实现
function myInstanceof(obj, tar) {
    // 获取对象的原型
    let proto = Object.getPrototypeOf(obj);
    // 获取构造函数的 prototype 对象
    let prototype = tar.prototype;
    // 对象原型走到头了
    if (!proto) return false;
    //判断原型是否相等
    return proto === prototype ? true : myInstanceof(proto, tar);
}

深拷贝

数组、对象的typeof结果都为'object',需要注意的是null的typeof结果也为'object',函数的typeof结果为'function',symbol无法直接遍历到。

function deepcopy(data) {
    if (data === null) return null;
    let obj = data instanceof Array ? [] : {};
    for (const [k, v] of Object.entries(data)) {
        obj[k] = typeof v === "object" ? deepcopy(v) : v;
    }
    for (const k of Object.getOwnPropertySymbols(data)) {
        obj[k] = typeof data[k] === "object" ? deepcopy(data[k]) : data[k];
    }
    return obj;
}

Object.create

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__,和new有点像。

function create(proto) {
    obj = {};
    obj.__proto__ = proto;
    return obj;
} //直译

function create(proto) {
    function F() {}
    F.prototype = proto;
    return new F();
} //推荐

new

new操作主要执行了以下操作:

  1. 新建一个空对象obj
  2. 将空对象的__proto__原型链指向构造函数的原型prototype
  3. 将构造函数的this指向obj,执行构造函数
  4. 返回构造函数的返回,如果返回为空则返回obj
function newObj(p, ...args) {
    const obj = Object.create(p.prototype);
    const result = p.apply(obj, args);
    return result || obj;
}

function Person(name) {
    this.name = name;
}
Person.prototype.getname = function () {
    console.log(this.name);
};
let person1 = new Person("san");
console.log(person1);
let person2 = newObj(Person, "si");
console.log(person2);

防抖

快速连续触发事件时,只有最后一次触发能成功执行。

function debounce(fn) {
    let timeout = null; // 创建一个标记用来存放定时器的返回值
    return function () {
        clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
        timeout = setTimeout(() => {
            // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
            fn.apply(this, arguments);
        }, 500);
    };
}
function sayHi() {
    console.log("防抖成功");
}

let inp = document.createElement("input");
inp.addEventListener("input", debounce(sayHi));
document.body.append(inp);

节流

一定时间内只会执行1次,上一次未处理完则不会执行。

function throttle(fn) {
    let canRun = true; // 通过闭包保存一个标记
    return function () {
        if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
        canRun = false; // 立即设置为false
        setTimeout(() => {
            // 将外部传入的函数的执行放在setTimeout中
            fn.apply(this, arguments);
            // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
            canRun = true;
        }, 500);
    };
}
function sayHi() {
    console.log("节流成功");
}
btn = document.createElement("button");
btn.innerHTML = "节流";
btn.addEventListener("click", throttle(sayHi));
document.body.append(btn);

call

将this挂载到目标上后执行,最后再删除

Function.prototype.myCall = function (context) {
    context = context || window;
    context.fn = this;
    let arg = [...arguments].slice(1);
    let result = context.fn(...arg);
    delete context.fn;
    return result;
};

apply

Function.prototype.myApply = function (context) {
    context = context || window;
    context.fn = this;
    let result;
    // 需要判断是否存储第二个参数
    // 如果存在,就将第二个参数展开
    if (arguments[1]) {
        result = context.fn(...arguments[1]);
    } else {
        result = context.fn();
    }
    delete context.fn;
    return result;
};

bind

Function.prototype.myBind = function (context) {
    let fn = this;
    var args = [...arguments].slice(1);
    // 返回一个函数
    return function () {
        return fn.apply(context, args.concat(...arguments));
    };
};

柯里化

将能够接收多个参数的函数转化为接收单一参数的函数,并且返回能接收余下参数且返回结果的新函数的技术

function add() {
    return Array.from(arguments).reduce((cur, item) => cur + item, 0);
}

function currying(fn) {
    let args = [];
    return function () {
        args.push(...arguments);
        return fn.apply(this, args);
    };
}

add = currying(add);
console.log(add(1)); //1
console.log(add(2)); //3
console.log(add(3, 4)); //10
console.log(add()); //10

反柯里化

方法借用,扩展方法使用范围,和call,apply,bind类似

function unCurrying(fn) {
    return function (obj, ...args) {
        return fn.apply(obj, args);
    };
}

push = unCurrying(Array.prototype.push);
obj = {};
push(obj, "lisi", "san");
console.log(obj);

// 等同于bind方法
obj = {};
push = Array.prototype.push.bind(obj);
push("lisi", "san");
console.log(obj);

双向绑定

通过Proxy实现对数据监听。

<input type="text" v-model="input" />
<input type="text" v-model="input" />
<div v-bind="input"></div>
<script>
    data = {};
    proxy = new Proxy(data, {
        get(obj, property) {},
        set(obj, property, value) {
            // model赋值
            // obj[property] = value;
            Reflect.set(...arguments);

            //视图响应
            let els = document.querySelectorAll(
                `[v-model="${property}"],[v-bind="${property}"]`
            );
            for (let el of els) {
                el.innerHTML = value;
                el.value = value;
            }
            return true;
        },
    });

    (function () {
        const els = document.querySelectorAll("[v-model]");
        for (const el of els) {
            el.addEventListener("keyup", function () {
                proxy[this.getAttribute("v-model")] = this.value;
            });
        }
    })();
</script>

数组扁平化

function flat(arr) {
    let res = [];
    for (let item of arr) {
        res = Array.isArray(item) ? res.concat(flat(item)) : res.concat(item);
    }
    return res;
}
最后修改:2021 年 08 月 27 日
你的赞赏是我前进的动力