深拷贝
在JavaScript赋值的时候,如果是引用类型拷贝的时候,变量拷贝的是地址,如果其中一个变量发生改变,另外一个变量也相应的发生改变,如果想达到改一处地方的值另外一处不变就要在堆里面再开辟一个新的内存用于保存数据,这就是深拷贝。
// 基础版
function deepCype(obj) {
if(typeof obj !== 'object' || obj === null) {
return obj;
}
// 首先创建一个存储对象
let result = {};
if (Array.isArray(obj)) {
result = [];
}
for (let key in obj) {
if(obj.hasOwnProperty(key)) {
result[key] = deepCype(obj[key]);
}
}
return result;
}
const obj1 = {
intro: '动物园',
principal: {
name: 'zhangsan',
age: 26,
gender: 'man'
},
animal: ['dog', 'panda', 'tiger']
}
const obj2 = deepCype(obj1);
console.log(obj1);
console.log(obj2);
手写一个bind函数
Function.prototype.bind1 = function() {
// 将参数拆解为数组,arguments是获取所有函数里面的参数
// Array.prototype.slice.call(arguments)能将具有length属性的对象(key值为数字)转成数组
const args = Array.from(arguments);
// const args = Array.prototype.slice.call(arguments);
// 获取 this (数组的第一项)
const t = args.shift();
// fn1,bind(...) 中的fn1
const self = this;
// 返回一个函数
return function() {
return self.apply(t, args);
}
}
function fn1(a, b, c) {
console.log('this: ', this);
console.log(a, b, c);
return 'this is fn1';
}
const f = fn1.bind1({x: 123}, 10, 20, 30);
console.log(f());
手写一个instanceOf函数
检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
const instanceOf = (A, B) => {
let p = A;
while (p) {
if (p === B.prototype) {
return true;
}
p = p.__proto__;
}
return false;
}
console.log(instanceOf(1, Number));
手写实现简易的jQuery
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>js基础知识</title>
</head>
<body>
<p>hello world</p>
<p>hello world</p>
<p>hello world</p>
<script src="./03jQuery.js"></script>
</body>
</html>
// index.js
// 使用class模拟jQuery
class jQuery {
constructor(selector) {
const result = document.querySelectorAll(selector);
// ES6 的新方法,将类数组对象或者可遍历对象转换成一个真正的数组,
// 要求对象里面必须要有 length 属性
const result = Array.from(document.querySelectorAll(selector))
const length = result.length;
for(let i = 0;i < length;i++) {
this[i] = result[i];
}
this.length = length;
this.selector = selector;
}
get(index) {
return this[index];
}
each(fn) {
for(let i = 0;i < this.length;i++) {
const elem = this[i];
fn(elem);
}
}
on(type, fn) {
return this.each(elem => {
elem.addEventListener(type, fn, false);
})
}
}
// 扩展
jQuery.prototype.dialog = function() { alter('dialog 方法的扩展') }
// 实例一个jQuery
const $p = new jQuery('p');
$p.get(0);
$p.each((elem) => console.log(elem.nodeName));
$p.on('click', () => {
alert('chicked');
})
// "造轮子",可以在真实的JQ中继承JQ
class myJQuery extends jQuery {
constructor(selector) {
super(selector);
}
// 扩展自己的方法
addClass() {}
style() {}
}
模拟一个 new 运算符
-
原理(分三步):
1、一个新对象被创建,它继承自 foo.prototype
2、构造函数 foo 被执行。执行的时候,相应的传参会被传入,同时上下文(this)会被指定为这个新实例。new foo 等同于 new foo(),只能用在不传递任何参数的情况
3、如果构造函数返回了一个"对象",那么这个对象会取代整个 new 出来的结果,如果构造函数没有返回对象,那么 new 出来的结果为步骤1创建的对象
模拟一个 new 运算符,可以使用 Object.create,Object.create 是通过设置原型对象
// 模拟一个 new 运算符
function imitationNew(fn) {
let o = Object.create(fn.prototype);
let k = fn.call(o);
console.log(k)
if(typeof k === 'object') {
return k;
} else {
return o;
}
}
function fn() {
this.name = 'oyzx';
this.sayName = function() {
return this.name;
}
}
var a = imitationNew(fn)
console.log(a)
var b = new fn();
console.log(b)
console.log(a.__proto__ === b.__proto__); // true
手写一个AJAX请求
- 使用XMLHttpRequest
function Ajax(method, url, data) {
return new Promise((resolve, reject) => {
let mData = ''
if(!!data) {
if(method === 'GET' || method === 'get') {
let index = 0;
for(let key in data) {
if(index === 0) {
mData += '?' + key + '=' + data[key]
} else {
mData += '&' + key + '=' + data[key]
}
index++
}
url+=mData
} else if(method === 'POST' || method === 'post') {
mData = JSON.stringify(data)
} else {
url+=mData
}
}
const xhr = new XMLHttpRequest();
xhr.open(method, url, true)
xhr.onreadystatechange = () => {
if(xhr.readyState === 4) {
if(xhr.status === 200 || xhr.status === 204) {
resolve(JSON.parse(xhr.responseText));
} else if(xhr.status === 404) {
reject(new Error('404 not found'));
}
}
}
xhr.send(mData)
})
}
Ajax('POST', 'https://www.baidu.com/', {name: 'oyzx', age: 18}).then(data => {
console.log(data);
});
防抖
- 在一定时间内多次调用函数,代码只会执行最后一次的调用
// 手写简单的防抖,以input输入为例
const input1 = document.getElementById('input');
// 只执行最后一个函数
function debounce(fn, delay = 500) {
let timer = null;
return function() {
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
}
}
input1.addEventListener('keyup', debounce(function() {
console.log(input1.value);
}), 600)
节流
- 当持续触发事件时,保证一定时间段内只调用一次事件处理函数
// 手写简单的节流,以拖拽div为例
const div1 = document.getElementById('div');
function throttle(fn, delay = 100) {
let timer = null;
return function() {
if(timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
}
}
div1.addEventListener('drag', throttle(function(e) {
console.log(e.offsetX, e.offsetY);
}), 200)
比较两个对象是否全相等
- 比较两个对象上的属性是否全部相等,与深拷贝类似
// 判断是否是数组对象
function isObject(obj) {
return typeof obj === 'object' && obj !== null;
}
// 判断全相等
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
// 值类型(注意,参与 equal 的一般不会是函数)
return obj1 === obj2;
}
if (obj1 === obj2) {
return true;
}
// 两个都是对象或者数组,而且不相等
// 1、先取出它们的 keys,比较长度
const obj1Keys = Object.keys(obj1);
const obj2Keys = Object.keys(obj2);
if (obj1Keys.length !== obj2Keys.length) return false;
// 2、以 obj2 为基准,使用递归进行深层次的比对
for (let key in obj1) {
const res = isEqual(obj1[key], obj2[key]);
if (!res) return false;
}
return true;
}
const obj1 = {
a: 'name',
b: 'age',
c: {
name: 'zhangsan'
}
}
const obj2 = {
a: 'name',
b: 'age',
c: {
name: 'lisi'
}
}
console.log(isEqual(obj1, obj2));