手写call、apply、bind函数
call实现
// 实现
Function.prototype.myCall = function(context) {
if(typeof context === 'undefined' || context === null) {
context = window
}
context.fn = this
let args = [...arguments].slice(1)
let result = context.fn(...args)
delete context.fn
return result
}
// 测试
function test() {
console.log(this.name)
}
const obj = {name: 123}
test.myCall(obj)
apply实现
// 实现
Function.prototype.myApply = function(context) {
if(typeof context === 'undefined' || context === null) {
context = window
}
context.fn = this
let args = arguments[1]
let result
if(args) {
result = context.fn(...args)
} else {
result = context.fn()
}
delete context.fn
return result
}
// 测试
function test() {
console.log(this.name)
}
const obj = {name: 456}
test.myApply(obj)
bind实现
// 实现
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
let _this = this
let args = [...arguments].slice(1)
return function F() {
if (this instanceof F) {
return _this.apply(this, args.concat([...arguments]))
}
return _this.apply(context, args.concat([...arguments]))
}
}
// 测试
function test() {
console.log(this.name)
}
const obj = {
name: 789
}
const p = test.myBind(obj)
p()
new
- 用new Object() 的方式新建了一个对象 obj
- 取出第一个参数,就是我们要传入的构造函数。此外因为 shift 会修改原数组,所以 arguments 会被去除第一个参数
- 将 obj 的原型指向构造函数,这样 obj 就可以访问到构造函数原型中的属性
- 使用 apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
- 判断返回的值是不是一个对象,如果是一个对象,我们就返回这个对象,如果没有,我们该返回什么就返回什么
// 实现
function objectFactory() {
var obj = new Object(),
Constructor = [].shift.call(arguments)
obj.__proto__ = Constructor.prototype
var ret = Constructor.apply(obj, arguments)
return typeof ret === 'object' ? ret : obj
}
// 测试
function Otaku (name, age) {
this.name = name;
this.age = age;
this.habit = 'Games';
}
Otaku.prototype.strength = 60;
Otaku.prototype.sayYourName = function () {
console.log('I am ' + this.name);
}
var person1 = new Otaku('Kevin', '18')
var person2 = objectFactory(Otaku, 'Kevin', '18')
console.log(person1)
console.log(person2)
instanceof
// 实现
function my_instanceof(obj, constructor) {
let constuctProto = constructor.prototype
while(true) {
if(obj.__proto__ === null) {
return false
}
if(obj.__proto__ === constuctProto) {
return true
}
obj = obj.__proto__
}
}
// 测试
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var mycar = new Car("Honda", "Accord", 1998);
console.log(my_instanceof(mycar, Car))
jsonp
// 实现
function jsonp(url, success) {
// 生成一个随机函数名
let fnName = "__jsonpFn" + Math.random().toString().replace(".", "");
// 创建script标签
let script = document.createElement('script')
// 设置script标签的src属性(需要判断url中是否带有参数)
script.src = url + (RegExp(/\?/).test(url) == true ? '&' : '?') + 'callback=' + fnName
// 默认异步执行
script.async = true
// 设置script标签的type属性
script.type = 'text/javascript'
// 绑定函数
window[fnName] = function (data) {
success && success(data)
// 调用回调函数后删除script标签
delete window[fnName];
document.body.removeChild(script);
}
// 将script标签加到页面中
document.body.appendChild(script)
}
// 测试
jsonp(
// 'https://api.asilu.com/weather/',
'https://api.asilu.com/weather/?city="武汉"',
function (data) {
console.log(data)
})
ajax
let xhr = new XMLHttpRequest()
xhr.open('get', url, true)
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
console.log('请求完成')
if(this.status >= 200 &&this.status<300){
console.log('成功')
}else{
console.log('失败')
}
}
}
xhr.onerror = function(e) {
console.log('连接失败')
}
xhr.send()
…扩展运算符
rest参数
的形式为 ...变量名
用于获取函数调用时传入的参数. 顾名思义, rest参数表示的是除了明确指定的参数外,剩下的参数的集合, 它的类型是Array
function restFunc(...args)
{
return args.length;
}
restFunc('This','is','rest','test'); // 输出4 参数的个数为4
function restFunc(firstArgs,...restArgs)
{
// true
console.log(Array.isArray(restArgs));
// 5 [6,7,8,9]
console.log(firstArgs,restArgs);
}
restFunc(5,6,7,8,9);
spread运算符 …
扩展运算符 …可以用于 数组的构造,也可以用于调用函数时,将一个数组用作函数参数(就是把这个数组转化为参数的列表,所以也就成了一个函数的参数)
// 数组合并
var arr1 = [1,3,5];
var arr2 = [2,4,6];
[...arr1,...arr2] // 输出 [1,3,5,2,4,6]
// 函数参数
function testFunc(x,y,z)
{
return x + y + z;
}
var args = [1,2,3];
testFunc(...args); //输出 6
// 浅拷贝
let a = {
age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
// 字符串转成数组
var arr = [...'hello']
console.log(arr)
//["h", "e", "l", "l", "o"]
深度克隆函数
var deepClone = function(target, obj){
for (var key in obj){
if (obj.hasOwnProperty(key)){
var val = obj[key];
if (typeof val === "object") {
target[key] = {};
deepClone(target[key], val)
}else {
target[key] = val;
}
}
}
return target;
}
promise
const PROMISE_STATUS = {
PENDING: 1,
RESOLVED: 2,
REJECTED: 3
};
let util = {
isFunc(fn) {
return typeof fn === "function";
},
isPromise(obj) {
return obj && this.isFunc(obj.then);
}
};
function MyPromise(exector) {
this.status = PROMISE_STATUS.PENDING;
this.val = undefined;
this.reason = undefined;
this.resolveCallback = undefined;
this.rejectCallback = undefined;
let resolve = val => {
if (this.status === PROMISE_STATUS.PENDING) {
this.status = PROMISE_STATUS.RESOLVED;
this.val = val;
if (util.isFunc(this.resolveCallback)) {
this.resolveCallback();
}
}
};
let reject = reason => {
if (this.status === PROMISE_STATUS.PENDING) {
this.status = PROMISE_STATUS.REJECTED;
this.reason = reason;
if (util.isFunc(this.rejectCallback)) {
this.rejectCallback();
}
}
};
// 在构造函数中立即执行exector,并由用户根据业务手动调用resolve或者reject
exector(resolve, reject);
}
MyPromise.prototype.then = function(onFull, onReject) {
return new MyPromise((resolve, reject) => {
let success = () => {
// 具体的返回值有Promise A+规范决定, 根据onFull 的结果处理后面一个then的调用,
// 1. 如果onFull结果为报错,进入catch,调用reject
// 2. 如果onFull结果为Promise,则根据promise的状态调用resolve或者reject
// 3. 如果onFull结果为普通值,则直接调用resolve
try {
let result = onFull(this.val);
if (util.isPromise(result)) {
result.then(
res => {
resolve(res);
},
e => {
reject(e);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
};
let error = () => {
let reason = util.isFunc(onReject) ? onReject(this.reason) : this.reason;
reject(reason);
};
if (this.status === PROMISE_STATUS.RESOLVED) {
success();
} else if (this.status === PROMISE_STATUS.REJECTED) {
error();
} else if (this.status === PROMISE_STATUS.PENDING) {
this.resolveCallback = success;
this.rejectCallback = error;
}
});
};
let myPromise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("setTimeout result");
}, 100);
});
let myP2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("async2 result");
}, 100);
});
myPromise
.then(res => {
console.log("res1: ", res);
throw 'onfull error'
return myP2;
})
.then(
res => {
console.log("res2: ", res);
return res;
},
err => {
console.log(err);
}
)
.then(res => {
console.log(res); // res2:
})
.then(res => {
console.log(res); // undefined
});
敏感词标记
var content = "XX阿斯顿撒旦撒旦和轻微oo饥饿和武as器就可怜见看完你去了哇";
var res = content.replace(/(XX|oo|as)/g, "<span>$1</span");
console.log(res);
实现字符串反转
var reverse = function(str){
return str.split("").reverse().join("");
}
实现数组随机排序
arr.srot(()=>{
return Math.random() > 0.5 ? 1 : -1;
})
取出数组中的最大值
// 不使用遍历
var res = Math.max.apply(null, arr);
实现lazyman
LazyMan(“Hank”)输出:
Hi! This is Hank!
LazyMan(“Hank”).sleep(10).eat(“dinner”)输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~
LazyMan(“Hank”).eat(“dinner”).eat(“supper”)输出
Hi This is Hank!
Eat dinner~
Eat supper~
LazyMan(“Hank”).sleepFirst(5).eat(“supper”)输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
实现flattenDeep
将输入[1,[2,[3,4]],5]转换成一维数组[1,2,3,4,5]
function flattenDeep(array){
let res = []
array.forEach(item=>{
if(Array.isArray(item)){
res = res.concat(flattenDeep(item));
}else {
res.push(item)
}
})
return res
}
格式化数字,三个一组,逗号分隔
用零宽正则实现
function toThousands(num) {
return (num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
}
其实还有更简单的方法
Number(1234556).toLocaleString()
手写一个函数实现斐波那契数列
const feibo= max=>{
let [a,b,i]= [0,1,1]
while(i++<=max) {
[a,b] = [b,a + b ]
console.log(b)
}
return a
}
防抖与节流
防抖定义:多次触发事件后,事件处理函数只执行一次,并且是在触发操作结束时执行。
防抖原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新定时。
节流定义:触发函数事件后,短时间间隔内无法连续调用,只有上一次函数执行后,过了规定的时间间隔,才能进行下一次的函数调用。
节流原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新定时。
/**
* 防抖函数
* @param fn 事件触发的操作
* @param duration 多少毫秒内连续触发事件,不会执行
*/
function debounce(fn,duration){
var timer
window.clearTimeout(timer)
timer = setTimeout(()=>{
fn.call(this)
},duration)
}
/**
* 节流函数
* @param fn 事件触发的操作
* @param duration 间隔多少毫秒需要触发一次事件
*/
function throttle(fn,duration){
let canIRun
if(!canIRun)return
fn.call(this)
canIRun = false
setTimeout(()=>{
canIRun = true
},duration)
}
js柯里化
const curry = (fn, arr = []) => (...args) => (
arg => arg.length === fn.length
? fn(...arg)
: curry(fn, arg)
)([...arr, ...args])
let curryTest=curry((a,b,c,d)=>a+b+c+d)
curryTest(1,2,3)(4) //返回10
curryTest(1,2)(4)(3) //返回10
curryTest(1,2)(3,4) //返回10
冒泡排序
function bubbleSort(arr) {
var len = arr.length
for (var i = 0; i < len - 1; i++) {
for (var j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
var temp = arr[j + 1]
arr[j + 1] = arr[j]
arr[j] = temp
}
}
}
return arr
}
// 测试
var arr = [31, 12, 3, 23, 45, 32, 1, 9, 17, 25]
console.log('冒泡排序', bubbleSort(arr))
快速排序
function quickSort(arr, left, right) {
var len = arr.length,
partitionIndex,
left = typeof left != 'number' ? 0 : left,
right = typeof right != 'number' ? len - 1 : right;
if (left < right) {
partitionIndex = partition(arr, left, right);
quickSort(arr, left, partitionIndex - 1);
quickSort(arr, partitionIndex + 1, right);
}
return arr;
}
// 分区操作
function partition(arr, left, right) {
var pivot = left, // 设定基准值(pivot)
index = pivot + 1;
for (var i = index; i <= right; i++) {
if (arr[i] < arr[pivot]) {
swap(arr, i, index);
index++;
}
}
swap(arr, pivot, index - 1);
return index - 1;
}
function swap(arr, i, j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// 测试
var arr = [31, 12, 3, 23, 45, 32, 1, 9, 17, 25]
console.log('快速排序', quickSort(arr))