实现new的过程
new操作符做了这些事:
- 创建一个全新的对象
- 这个对象的
__proto__要指向构造函数的原型prototype - 执行构造函数,使用
call/apply改变 this 的指向 - 返回值为
object类型则作为new方法的返回值返回,否则返回上述全新对象
function myNew(fn, ...args) {
// 基于原型链 创建一个新对象
let newObj = Object.create(fn.prototype);
// 添加属性到新对象上 并获取obj函数的结果
let res = fn.apply(newObj, args); // 改变this指向
// 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象
return typeof res === 'object' ? res: newObj;
}
// 用法
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function() {
console.log(this.age);
};
let p1 = myNew(Person, "poety", 18);
console.log(p1.name);
console.log(p1);
p1.say();
实现 getValue/setValue 函数来获取path对应的值
// 示例
var object = {
a: [{
b: {
c: 3 } }] }; // path: 'a[0].b.c'
var array = [{
a: {
b: [1] } }]; // path: '[0].a.b[0]'
function getValue(target, valuePath, defaultValue) {
}
console.log(getValue(object, "a[0].b.c", 0)); // 输出3
console.log(getValue(array, "[0].a.b[0]", 12)); // 输出 1
console.log(getValue(array, "[0].a.b[0].c", 12)); // 输出 12
实现
/**
* 测试属性是否匹配
*/
export function testPropTypes(value, type, dev) {
const sEnums = ['number', 'string', 'boolean', 'undefined', 'function']; // NaN
const oEnums = ['Null', 'Object', 'Array', 'Date', 'RegExp', 'Error'];
const nEnums = [
'[object Number]',
'[object String]',
'[object Boolean]',
'[object Undefined]',
'[object Function]',
'[object Null]',
'[object Object]',
'[object Array]',
'[object Date]',
'[object RegExp]',
'[object Error]',
];
const reg = new RegExp('\\[object (.*?)\\]');
// 完全匹配模式,type应该传递类似格式[object Window] [object HTMLDocument] ...
if (reg.test(type)) {
// 排除nEnums的12种
if (~nEnums.indexOf(type)) {
if (dev === true) {
console.warn(value, 'The parameter type belongs to one of 12 types:number string boolean undefined Null Object Array Date RegExp function Error NaN');
}
}
if (Object.prototype.toString.call(value) === type) {
return true;
}
return false;
}
}
const syncVarIterator = {
getter: function (obj, key, defaultValue) {
// 结果变量
const defaultResult = defaultValue === undefined ? undefined : defaultValue;
if (testPropTypes(obj, 'Object') === false && testPropTypes(obj, 'Array') === false) {
return defaultResult;
}
// 结果变量,暂时指向obj持有的引用,后续将可能被不断的修改
let result = obj;
// 得到知道值
try {
// 解析属性层次序列
const keyArr = key.split('.');
// 迭代obj对象属性
for (let i = 0; i < keyArr.length; i++) {
// 如果第 i 层属性存在对应的值则迭代该属性值
if (result[keyArr[i]] !== undefined) {
result = result[keyArr[i]];
// 如果不存在则返回未定义
} else {
return defaultResult;
}
}
} catch (e) {
return defaultResult;
}
// 返回获取的结果
return result;
},
setter: function (obj, key, val) {
// 如果不存在obj则返回未定义
if (testPropTypes(obj, 'Object') === false) {
return false;
}
// 结果变量,暂时指向obj持有的引用,后续将可能被不断的修改
let result = obj;
try {
// 解析属性层次序列
const keyArr = key.split('.');
let i = 0;
// 迭代obj对象属性
for (; i < keyArr.length - 1; i++) {
// 如果第 i 层属性对应的值不存在,则定义为对象
if (result[keyArr[i]] === undefined) {
result[keyArr[i]] = {
};
}
// 如果第 i 层属性对应的值不是对象(Object)的一个实例,则抛出错误
if (!(result[keyArr[i]] instanceof Object)) {
throw new Error('obj.' + keyArr.splice(0, i + 1).join('.') + 'is not Object');
}
// 迭代该层属性值
result = result[keyArr[i]];
}
// 设置属性值
result[keyArr[i]] = val;
return true;
} catch (e) {
return false;
}
},
};
使用promise来实现
创建 enhancedObject 函数
const enhancedObject = (target) =>
new Proxy(target, {
get(target, property) {
if (property in target) {
return target[property];
} else {
return searchFor(property, target); //实际使用时要对value值进行复位
}
},
});
let value = null;
function searchFor(property, target) {
for (const key of Object.keys(target)) {
if (typeof target[key] === "object") {
searchFor(property, target[key]);
} else if (typeof target[property] !== "undefined") {
value = target[property];
break;
}
}
return value;
}
使用 enhancedObject 函数
const data = enhancedObject({
user: {
name: "test",
settings: {
theme: "dark",
},
},
});
console.log(data.user.settings.theme); // dark
console.log(data.theme); // dark
以上代码运行后,控制台会输出以下代码:
dark
dark
通过观察以上的输出结果可知,使用
enhancedObject函数处理过的对象,我们就可以方便地访问普通对象内部的深层属性。
用正则写一个根据name获取cookie中的值的方法
function getCookie(name) {
var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]*)'));
if (match) return unescape(match[2]);
}
- 获取页面上的
cookie可以使用document.cookie
这里获取到的是类似于这样的字符串:
'username=poetry; user-id=12345; user-roles=home, me, setting'
可以看到这么几个信息:
-
每一个cookie都是由
name=value这样的形式存储的 -
每一项的开头可能是一个空串
''(比如username的开头其实就是), 也可能是一个空字符串' '(比如user-id的开头就是) -
每一项用
";"来区分 -
如果某项中有多个值的时候,是用
","来连接的(比如user-roles的值) -
每一项的结尾可能是有
";"的(比如username的结尾),也可能是没有的(比如user-roles的结尾)
- 所以我们将这里的正则拆分一下:
-
'(^| )'表示的就是获取每一项的开头,因为我们知道如果^不是放在[]里的话就是表示开头匹配。所以这里(^| )的意思其实就被拆分为(^)表示的匹配username这种情况,它前面什么都没有是一个空串(你可以把(^)理解为^它后面还有一个隐藏的'');而|表示的就是或者是一个" "(为了匹配user-id开头的这种情况) -
+name+这没什么好说的 -
=([^;]*)这里匹配的就是=后面的值了,比如poetry;刚刚说了^要是放在[]里的话就表示"除了^后面的内容都能匹配",也就是非的意思。所以这里([^;]*)表示的是除了";"这个字符串别的都匹配(*应该都知道什么意思吧,匹配0次或多次) -
有的大佬等号后面是这样写的
'=([^;]*)(;|$)',而最后为什么可以把'(;|$)'给省略呢?因为其实最后一个cookie项是没有';'的,所以它可以合并到=([^;]*)这一步。
- 最后获取到的
match其实是一个长度为4的数组。比如:
[
"username=poetry;",
"",
"poetry",
";"
]
- 第0项:全量
- 第1项:开头
- 第2项:中间的值
- 第3项:结尾
所以我们是要拿第2项match[2]的值。
- 为了防止获取到的值是
%xxx这样的字符序列,需要用unescape()方法解码。
JavaScript 手写面试题集锦

这篇博客整理了JavaScript的常见手写面试题,包括实现`new`过程、正则获取cookie值、URL Params解析、字符串不重复子串、身份证验证等。还涉及Promise、Redux中间件、模板字符串解析、版本号排序、本地缓存管理等功能的实现。
最低0.47元/天 解锁文章
773

被折叠的 条评论
为什么被折叠?



