反复刷这些javascript手写题,我又变强了
Posted _阿锋丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反复刷这些javascript手写题,我又变强了相关的知识,希望对你有一定的参考价值。
文章目录
下面的promise有我敲写分析的过程视频
数组原型方法的实现
其实下面的这几个方法的写法差不多,参数也只有一些细微差别,但是要考虑到,如果是要返回一个新的数组的方法比如map,reduce都要用到深拷贝
forEach
这里主要需要注意的就是第一个参数是回调函数
,然后这个回调 函数也会有三个参数,分别是元素值,元素索引,遍历数组本身,第二个参数就是数组遍历时this的指向地址,下面的filter,map都是这样的参数类型
Array.prototype._forEach = function (cb, ...ret)
if (typeof cb !== 'function')
console.log('first param must be function');
if (!Array.isArray(this))
throw new Error('is not a array');
// console.log(cb);
// console.log(ret[0]);
let arr = this;
let len = arr.length;
let arg = ret[0] || window;
//fo循环每次都可以调用这个回调函数的一些逻辑处理
for (let i = 0; i < len; i++)
cb.call(arg, arr[i], i, arr);
;
const arr = [1, 2, 3];
const obj = a: 1 ;
// arr.forEach(function (item, index, array)
// console.log(this); //可以传第二个参数,如果第二个参数是obj,那么this就是
// // 指向的是obj
// console.log(item, index, array);
// , obj);
arr._forEach(function (item, index, arr)
console.log(item, index, arr);
, obj);
map
这里我觉得唯独需要注意的就是,返回一个新的数组,需要用到深拷贝
Array.prototype._map = function (cb, ret)
let arr = this;
let res = [];
if (!Array.isArray(this))
throw new Error('is not a array');
for (let i = 0; i < arr.length; i++)
// 如果数据里面有对象等引用类型,那么就需要执行深拷贝
//res.push(cb.call(ret, deepClone(arr[i]), i, arr));
res.push(cb.call(ret, arr[i], i, arr));
return res;
;
const arr = [1, 2, 3];
const obj = a: 1 ;
let newArr = arr._map(function (item, index, arr)
console.log(item, index, arr);
return item * 2;
, obj);
console.log(newArr);
console.log(arr);
filter
逻辑上的处理差不多和上面的
Array.prototype._filter = function (cb)
let arr = this;
let arg = arguments[1];
let res = [];
if (!Array.isArray(arr))
throw new Error('is not a array');
for (let i = 0; i < arr.length; i++)
let flag = cb.call(arg, arr[i], i, arr);
if (flag)
res.push(arr[i]);
return res;
;
const arr = [1, 2, 3];
let newArr = arr._filter(function (item)
return item > 1;
);
console.log(newArr);
every
会写这个了肯定会写some方法了
Array.prototype._every = function (cb)
let arr = this;
let arg = arguments[1] || window;
for (let i = 0; i < arr.length; i++)
let flag = cb.call(arg, arr[i], i, arr);
if (!flag)
return false;
return true;
;
const arr = [1, 2, 3];
let res = arr._every(function (item, index, array)
return item > 0;
);
console.log(res);
reduce
这个也是需要深拷贝的
Array.prototype._reduce = function (cb, initialValue)
let arr = this;
let item;
for (let i = 0; i < arr.length; i++)
// item = deepClone(arr[i]); 这里是需要深拷贝的
initialValue = cb.call(arr, initialValue, arr[i], i, arr);
return initialValue;
;
const arr = [1, 3, 3];
let newArr = arr._reduce(function (pres, cur)
return pres + cur;
, 0);
console.log(newArr);
对象原型方法的实现
object.assign
对于Object.assign需要注意的一点就是,合并对象的属性时,后面合并的有相同属性会覆盖掉前面合并的相同属性,而且切记,它只能合并可枚举的属性,也就是enumable为true的属性,思路很简单,由于object.assgin是浅拷贝只用简单的复制语句就能复制指针,于是第一层遍历合并的一些对象,然后第二层遍历对象的各个属性然后进行合并
// 浅拷贝
Object._assign = function (target, ...source)
if (target == null)
throw new TypeError('Cannot convert undefined or null to object');
// console.log(source);
let ret = Object(target);
source.forEach((obj) =>
if (obj != null)
for (let key in obj)
console.log(key);
// 可枚举的属性才会拷贝
if (obj.hasOwnProperty(key))
ret[key] = obj[key];
);
;
const a =
x: 1,
y:
z: 1,
,
;
const b =
x: 2,
name: 'af',
hobbies:
sports: 'basketball',
music:
style: ['fuck', 'blues'],
instruments: ['guitar'],
,
,
;
// Object.assign(a, b);
Object._assign(a, b);
console.log(a);
附加一到关于Object.assign的题
// 面试题
const v1 = 123;
const v2 = '123';
const v3 = true;
const v4 = function test() ;
const v5 = Object.assign(, v1, v2, v3, v4);
console.log(v5);
// 打印结果 :0: "1", 1: "2", 2: "3"
// 结果很巧妙:这是因为Object.assign会自动将不是对象的方法转化为对象
// 然后只会合并可枚举的属性,而上面拥有可枚举的属性的对象就只有String对象
const r1 = new Number(v1);
const r2 = new String(v2);
const r3 = new Boolean(v3);
const r4 = new Function(v4);
console.log(r1);
console.log(r2);
console.log(r3);
console.log(r4);
for (let key in r1)
console.log(key);
for (let key in r2)
console.log(key); //唯独只有这里可以打印
for (let key in r3)
console.log(key);
for (let key in r4)
console.log(key);
object.create
用这个方法能够非常简单的把组合继承变为寄生组合继承,由于它是利用了父构造函数的原型对象,创建一个新的原型对象来使子构造函数的__proto__ 来指向实现继承,相对于组合继承少了一次访问构造函数的有点
第二个参数是设置对象自己的属性,而且必须是一个对象descripter描述器,所以直接可以用Object.definepropert实现
// 第二个参数是设置对象自己的属性,而且必须是一个对象descripter描述器
Object._create = function (proto, propertyObject = undefined)
if (typeof proto !== 'object' && typeof proto !== 'function')
throw new TypeError(
'Object prototype may only be Object or null to Object',
);
if (propertyObject == null)
new TypeError('cannot convert undefined or null to Object');
// 关键就这三行
function F()
F.prototype = proto;
const obj = new F();
if (propertyObject !== undefined)
Object.defineProperty(obj, propertyObject);
if (proto === null)
// 创建一个没有原型的对象 Object.create(null)
obj._proto_ = null;
return obj;
;
手写promise
這個相比不用多说有多重要了,天天用到的,所以在学习实现手写之前必须掌握好的它的一些特点和细节。
视频讲解地址
由于细节有点多,先写个只完成基本功能的,还有一些promise方法,没写我准备会在b站上发布一个视频来解析我的代码并贴在这里,如果一直没发布希望有人评论提醒我
下面代码有详细注释
class Af
// 先定义三种状态
static PENGDING = 'pengding';
static FULLFILLED = 'fullfilled';
static REJECTED = 'rejected';
constructor(executor)
this.status = Af.PENGDING;
this.value = '';
// 把状态改变后的函数放到下面数组中执行
// 等状态改变之后再拿出来执行
this.callbacks = [];
// 因为resolve和reject方法是在executor中调用,作用域也是executor的作用域
// 这样会造成this指向window,现在我么使用的是class定义,this会是undefined
// 所以下面绑定了this
try
executor(this.resolve.bind(this), this.reject.bind(this));
catch (error)
// 当执行者出现异常时触发拒绝状态
this.reject(error);
resolve(value)
// 状态只能改变一次所以在resolve和reject都需要添加状态条件判断
// 而且之能是 pending->fullfilled pending->rejected
if (this.status == Af.PENGDING)
this.status = Af.FULLFILLED;
this.value = value;
// 添加处理callback方法的代码 异步处理s
setTimeout(() =>
this.callbacks.forEach((callback) => callback.onFullfilled(value));
);
reject(value)
// 状态只能改变一次所以在resolve和reject都需要添加状态条件判断
// 而且之能是 pending->fullfilled pending->rejected
if (this.status == Af.PENGDING)
this.status = Af.REJECTED;
this.value = value;
// 添加处理callback方法的代码 异步处理
setTimeout(() =>
this.callbacks.forEach((callback) => callback.onRejected(value));
);
// onFullfilled,onRjected即成功和错误时的回调函数
then(onFullfilled, onRejected)
// then可以有两个参数,即成功和错误时的回调函数
// then的函数参数都不是必须的,所以需要设置默认为函数
// 用于处理当没有传递时的情况
if (typeof onFullfilled !== 'function')
onFullfilled = (value) => value;
if (typeof onRejected !== 'function')
onRejected = (value) => value;
// 返回一个新的promise对象 ,使.then能够链式调用
return new Af((resolve, reject) =>
// .then处理pengding状态(如果rejected被异步回调函数包裹,比如用setTimeOut包裹的
// 时候,状态是异步被改变的,那么没有下面Pending状态的处理,将不会执行.then方法,因为
// 定时函数之前一直没有执行resolve方法)
if (this.status == Af.PENGDING)
// 将then方法的回调函数添加到 callbacks 数组中,用于异步执行
this.callbacks.push(
onFullfilled: (value) =>
try
let res = onFullfilled(value);
resolve(res);
catch (error)
reject(error);
,
onRejected: (value) =>
try
let res = onRejected(value);
reject(res);
catch (error)
reject(error);
,
);
// .then处理fullfilled状态
if (this.status == Af.FULLFILLED)
// setTimeOut的作用是使.then回调函数其异步执行
setTimeout(() =>
try
let res = onFullfilled(this.value);
resolve(res);
catch (error)
reject(error);
);
// .then处理rejected状态
if (this.status == Af.REJECTED)
setTimeout(() =>
try
let res = onRejected(this.value);
reject(res);
catch (error)
反复刷这些javascript手写题,我又变强了
反复刷这些javascript面试手写题,我感觉我真的变强了