数据类型
一般认为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操作主要执行了以下操作:
- 新建一个空对象obj
- 将空对象的
__proto__
原型链指向构造函数的原型prototype
- 将构造函数的
this
指向obj,执行构造函数 - 返回构造函数的返回,如果返回为空则返回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;
}