手写防抖(Debouncing)和节流(Throttling)
防抖(Debouncing)实现
- 典型例子:限制
鼠标连击
触发、监听用户输入
。
解释:当一次事件发生后,事件处理器要等一定阀值的时间,如果这段时间过去后,再也没有事件发生,就处理最后一次发生的事件。如果还差
0.01
秒到达指定事件,这是又有一次事件触发,那么之前的等待作废,需要重新再次等待指定时间。
//防抖动函数
function debounce(fn, wait = 50, immediate) {
let timer;
return function() {
if (immediate) {
fn.apply(this, arguments)
}
if (timer) clearTimeout(timer)
timer = setTimeout() => {
fn.apply(this, arguments)
},wait)
}
}
节流(Throttling)实现
解释:可以理解为事件再一个管道中传输,加上这个节流阀之后,事件的流速就会减慢,节流可以将一个函数的调用频率限制再一定阀值内,例如 1s ,那么 1s 内这个函数一定不会被调用两次。
//简单的节流函数
function throttle(fn, wait) {
let prev = new Date();
return function() {
const args = arguments;
const now = new Date();
if (now - prev > wait) {
fn.apply(this, args);
prev = new Date();
}
}
}
手写一个JS深拷贝
- 著名乞丐版
var newObj = JSON.parse ( JSON.stringify ( someObj ) );
- 面试够用版
function deepCopy(obj) {
if (typeof obj == "object") {
//复杂数据类型
var result = obj.constructor == Array ? [] : {};
for (let i in obj) {
result[i] = typeof obj[i] == "object" ? deepCopy(obj[i] : obj[i])
}
} else {
//简单数据类型
var result = obj
}
return result
}
手写一个Promise
- 三种状态
pending | fulfilled(resolved) | rejected
- 当处于
pending
状态的时候,可以转移到fulfilled(resolved)
或者rejected
状态 - 当处于
fulfilled(resolved)
状态或者rejected
状态时候,就不可变。 - 必须有一个
then
的异步执行方法,then
接受两个参数onFulfilled、onRejected
基础版 代码实现:
function myPromise(constructor){
let self=this;
self.status="pending" //定义状态改变前的初始状态
self.value=undefined;//定义状态为resolved的时候的状态
self.reason=undefined;//定义状态为rejected的时候的状态
function resolve(value){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
//同时再myPromise的原型上定义链式调用的then方法:
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
实现 instanceof
function instanceof (left, right) {
let proto = left.__proto__;
let prototype = right.prototype
while (true) {
if (proto === null) return false
if (proto === prototype) return true
proto = proto.__proto__;
}
}
js继承的几种实现方式
首先,我们需要定义一个父类:
//定义一个动物类
function Animal (name) {
//属性
this.name = name || 'Animal';
//实例方法
this.sleep = function() {
console.log(this.name + 'i'm sleeping')
}
}
//原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + 'i'm eating' + food)
}
- 原型链继承
function Parent2 () {
this.name = 'parent2'
}
function Child2 () {
this.sex = 'male'
}
Child2.prototype = new Parent2()
console.log(new Child2())
- 构造函数继承
function Parent1 () {
this.name = 'parent1'
}
Parent1.prototype.say = function(){};
function Child1() {
Parent1.call(this)
this.sex = 'male'
}
console.log(new Child1())
- 实例继承
- 拷贝继承
- 组合继承
function Cat(name) {
Animal.call(this)
this.name = name || 'tom'
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
//Cat.prototype = Animal.prototype
// Text code
var cat = new Cat()
console.log(cat.name)
console.log(cat.sleep())
console.log(cat instanceof Animal) //true
console.log(cat instanceof Cat) //true
- 寄生组合继承
function Cat(name) {
Animal.call(this);
this.name = name || 'Tom'
}
(function(){
// 创建一个没有实例的类
var Super = function() {
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
})();
Cat.prototype.constructor = Cat;
// Text Code
var cat = new Cat();
console.log(cat.name)
console.log(cat.sleep)
console.log(cat instanceof Animal) //true
console.log(cat instanceof Cat) //true
js函数柯里化
//求和
let add = function (a, b, c) {
return a + b +c
}
function curry(fn) {
//闭包
//缓存除函数fn之外的所有参数
let args = Array.prototype.slice.call(arguments, 1)
return function() {
//连接已经缓存的老的参数和新传入的参数(就是把每次传入的参数都先保存下来,但是并不执行)
let newArgs = args.concat(Array.from(arguments))
if (newArgs.length < fn.length) {
return curry.call(this, fn, ...newArgs)
} else {
//调用
return fn.apply(this, newArgs)
}
}
}
let f = curry(add)
console.log('柯里化',f(1)(2)(3))
console.log('柯里化',f(1, 2)(3))
console.log('柯里化',f(1, 2, 3))
原生js封装ajax
function ajax(options) {
var xhr = null;
var params = formsParams(options, data);
//创建对象
if (window.XMLHttpRequest()) {
xhr = new XMLHttpRequest()
} else {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
//连接
if (options.type === "GET") {
xhr.open(options.type, options.url + "?" +params, options.async);
xhr.send(null)
} else if (options.type === "POST") {
xhr.open(options.type, options.url, options.async);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(params);
}
//判断状态
xhr.onreadystateChange = function() {
if (xhr.readyState === 4 && xhr.status ===200) {
options.success(xhr.responseText);
}
function formParams(data) {
var arr = [];
for (var prop in data) {
arr.push(prop + "=" + data[prop]);
}
return arr.join("&");
}
}
}
//调用
ajax({
url: "time.php",
type: "POST",
async: true,
data: {
name: "Tom",
age: 18
},
success: function(data) {
console.log(data);
}
})
手写jsonp
跨域通信的几种方式:jsonp、hash、postMessage、webSocket、CORS
(function (window,document) {
"use strict";
var jsonp = function (url,data,callback) {
// 1.将传入的data数据转化为url字符串形式
// {id:1,name:'zhangsan'} => id=1&name=zhangsan
var dataString = url.indexof('?') == -1? '?': '&';
for(var key in data){
dataString += key + '=' + data[key] + '&';
};
// 2 处理url中的回调函数
// cbFuncName回调函数的名字 :my_json_cb_名字的前缀 + 随机数(把小数点去掉)
var cbFuncName = 'my_json_cb_' + Math.random().toString().replace('.','');
dataString += 'callback=' + cbFuncName;
// 3.创建一个script标签并插入到页面中
var scriptEle = document.createElement('script');
scriptEle.src = url + dataString;
// 4.挂载回调函数
window[cbFuncName] = function (data) {
callback(data);
// 处理完回调函数的数据之后,删除jsonp的script标签
document.body.removeChild(scriptEle);
}
// 5.append到页面中
document.body.appendChild(scriptEle);
}
// 因为jsonp是一个私有函数外部不能调用,所有jsonp函数作文window对象的一个方法,供外部调用
window.$jsonp = jsonp;
})(window,document)