for (var i = 0; i <= 3; i++) {
setTimeout(function() {
console.log(i);
}, 10);
} // 4~4
for (var i = 0; i <= 3; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 10);
})(i);
} // 0~3
for (let i = 0; i <= 3; i++) {
setTimeout(function() {
console.log(i);
}, 10);
} // 0~3, let为块级作用域
支持变量和对象解析和换行
const b = "lubai";
const a = `${b} - xxxx`;
const c = `我是换行
我换行了!
`;
console.log(a, b, c);
面试题:编写render函数,实现template render功能
// 要求
const year = "2022";
const month = "10";
const day = "01";
let template = "${year}-${month}-${day}";
let context = { year, month, day };
const str = render(template)({ year, month, day });
console.log(str); // 2022-10-01
// 实现:高阶函数(函数返回函数)
function render(template) {
return function(content) {
return template.replace(/\$\{(.*?)\}/g, (match, key) => {
console.log(match, key);
return content[key];
});
};
}
/**
.*表示:任意值
?表示:匹配前面的表达式0或1个,或指定非贪婪限定符
表达式 .* 就是单个字符匹配任意次,即贪婪匹配。
表达式 .*? 是满足条件的情况只匹配一次,即最小匹配。
match: ${year} ${month} ${day}
key: year month day
*/
参考资料:replace的mdn资料
replace() 方法返回一个由替换值(replacement)替换部分或所有的模式(pattern)匹配项后的新字符串。模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要调用的回调函数。如果pattern是字符串,则仅替换第一个匹配项。
str.replace(regexp|substr, newSubStr|function)
替换字符串可以插入下面的特殊变量名:
变量名 | 代表的值 |
---|---|
$$ | 插入一个 “$”。 |
$& | 插入匹配的子串。 |
$` | 插入当前匹配的子串左边的内容。 |
$’ | 插入当前匹配的子串右边的内容。 |
$n | 假如第一个参数是 RegExp(opens new window)对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始。如果不存在第 n个分组,那么将会把匹配到到内容替换为字面量。比如不存在第3个分组,就会用“$3”替换匹配到的内容。 |
$ | 这里_Name_ 是一个分组名称。如果在正则表达式中并不存在分组(或者没有匹配),这个变量将被处理为空字符串。只有在支持命名分组捕获的浏览器中才能使用。 |
函数的参数:
变量名 | 代表的值 |
---|---|
match | 匹配的子串。(对应于上述的$&。) |
p1,p2, … | 假如replace()方法的第一个参数是一个RegExp(opens new window)对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)例如,如果是用 /(\a+)(\b+)/ 这个来匹配,p1 就是匹配的 \a+,p2 就是匹配的 \b+。 |
// 基础类型解构
let [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1,2,3
// 对象数组解构
let [a, b, c] = [{ name: "1" }, { name: "2" }, { name: "3" }];
console.log(a, b, c); // { name: "1" }, { name: "2" }, { name: "3" }
// ...解构
let [head, ...tail] = [1, 2, 3, 4];
console.log(head, tail); // 1, [2, 3, 4]
// 嵌套解构
let [a, [b], d] = [1, [2, 3], 4];
console.log(a, b, c); // 1,2,4
// 解构不成功为undefined
let [a, b, c] = [1];
console.log(a, b, c); // 1,undefined,undefined
// 解构默认赋值
let [a = 1, b = 2] = [3];
console.log(a, b); // 3,2
// 对象属性解构
let { f1, f2 } = { f1: "test1", f2: "test2" };
console.log(f1, f2); // test1, test2
// 可以不按顺序,这是数组解构和对象解构的区别之一
let { f2, f1 } = { f1: "test1", f2: "test2" };
console.log(f1, f2); // test2, test1
// 解构对象重命名
let { f1: rename, f2 } = { f1: "test1", f2: "test2" };
console.log(rename, f2); // test1, test2
// 嵌套解构
let { f1: { f11 } } = { f1: { f11: "test11", f12: "test12" } };
console.log(f11); // test11
// 默认值
let { f1 = "test1", f2: rename = "test2" } = { f1: "current1", f2: "current2" };
console.log(f1, rename); // current1, current2
针对可迭代对象的Iterator接口,通过遍历器按顺序获取对应的值进行赋值。
Iterator是一种接口,为各种不一样的数据解构提供统一的访问机制。任何数据解构只要由Iterator接口,就能通过遍历操作,依次按顺序处理数据解构内所有的成员。使用for of的语法遍历数据解构时,自动寻找Iterator接口。
可迭代对象时Iterator接口的实现,有两个协议:可迭代协议和迭代器协议。
const obj = {
count: 0,
[Symbol.iterator]: () => {
return {
next: () => {
obj.count++;
if (obj.count <= 10) {
return {
value: obj.count,
done: false
};
} else {
return {
value: undefined,
done: true
};
}
}
};
}
};
const iterable = {
0: "a",
1: "b",
2: "c",
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (const item of iterable) {
console.log(item); // 'a', 'b', 'c'
}
let obj = {
a: "test1",
b: "test2"
};
for (const key in obj) {
console.log(key, obj[key]); // 遍历数组时,key为数组下标字符串;遍历对象,key为对象字段名
}
返回一个给定对象的自身可枚举属性组成的数组
返回一个给定对象的自身可枚举属性值的数组
返回一个给定对象的自身可枚举键值对数组
const obj = { a: 1, b: 2 };
const keys = Object.keys(obj);
const values = Object.values(obj);
const entries = Object.entries(obj);
// 手写getKeys
function getKeys(obj) {
const result = [];
for (const prop in obj) {
if (Object.hasOwnProperty.call(object, prop)) {
result.push(prop);
}
}
return result;
}
返回一个给定对象拥有的可枚举或不可枚举属性名称字符串的数组
Object.prototype.aa = "1111";
const testData = {
a: 1,
b: 2
};
for (const key in testData) {
console.log(key); // a b aaa
}
console.log(Object.getOwnPropertyNames(testData)); // ['a', 'b']
object包括属性和方法,其中属性分为数据属性和访问器属性
Object.defineProperty(obj, propertyName, descriptor) // 用于对象的单个属性定义,参数:对象,属性名称,描述符对象
Object.defineProperties(obj, descriptor) // 用于对象多个属性定义,参数:对象、描述符对象(属性与添加或修改的属性——对象)
let test = {};
Object.defineProperty(test, "name", {
configurable: true,
enumerable: true,
writable: true,
value: "Jian"
});
let t = 0;
Object.defineProperties(test, {
age: {
configurable: true,
enumerable: true,
get() {
return t;
},
set(newVal) {
t = newVal;
}
},
sex: {
configurable: true,
enumerable: true,
// writable: true,
value: "male"
}
});
test.sex = 2;
console.log("test.name", test.name); // Jian
console.log("test.age", test.age); // 0
console.log("test.sex", test.sex); // male, test.sex不生效
创建一个新对象,两个参数:
const person = {
isHuman: false,
printIntroduction: function() {
console.log(`My name is ${this.name}. ${this.isHuman}`);
}
};
const me = Object.create(person);
me.name = "scp";
me.isHuman = true;
me.printIntroduction(); // My name is scp. true
console.log(person); // { isHuman: false, printIntroduction: [Function: printIntroduction] }
function Person(name, sex) {
this.name = name;
this.sex = sex;
}
const b = Object.create(Person.prototype, {
name: {
value: "coco",
writable: true,
configurable: true,
enumerable: true
},
sex: {
enumerable: true,
get: function() {
return "hello sex";
},
set: function(val) {
console.log("set value:", val);
}
}
});
console.log(b.name); // coco
console.log(b.sex); // hello sex
Object.create(null)创建一个对象,但这个对象的原型为null,即Fn.prototype = null
返回指定对象的属性描述符对象
const obj1 = {};
Object.defineProperty(obj1, "p", {
value: "good",
writable: false
});
console.log(Object.getOwnPropertyDescriptor(obj1, "p"));
console.log(Object.getOwnPropertyDescriptors(obj1));
/**
{
value: 'good',
writable: false,
enumerable: false,
configurable: false
}
{
p: {
value: 'good',
writable: false,
enumerable: false,
configurable: false
}
}
*/
合并对象,接收一个目标对象和一个或多个源对象作为参数,将每个源对象中可枚举(Object.propertyIsEnumerable()返回true)和自有属性(Object.hasOwnProperty()返回true)复制到目标对象。
let dest = {};
let src = { id: "src", a: {} };
let result = Object.assign(dest, src);
console.log(dest === result); //true
console.log(result); // {id: 'src', a: {}}
console.log(dest.a === src.a); // true
dest = {
set a(val) {
console.log(`Invoked dest setter with param ${val}`);
}
};
src = {
get a() {
console.log("Invoked src getter");
return "foo";
}
};
Object.assign(dest, src);
// Invoked src getter
// Invoked dest setter with param foo
console.log(dest);
// {set a(val) {}}
console.log(src);
// {get a() {}}
按照一个可指定的深度递归遍历数组,将所有元素与遍历到的子数组中的元素合并为一个新数组返回
const arr = [1, 2, [3, (4)[(5, 6)]]];
arr.flat(); // [1,2,3,4,[5,6]]
arr.flat(2); // [1,2,3,4,5,6]
arr.flat(Infinity); // [1,2,3,4,5,6]
// 手写flat
Array.prototype.flat = function(d = 1) {
if (d > 0) {
return this.reduce((res, val) => {
if (Array.isArray(val)) {
res = res.concat(val.flat(d - 1));
} else {
res = res.concat(val);
}
return res;
});
} else {
return this.slice();
}
};
// 全部打平
function flat(arr) {
let res = [];
let length = arr.length;
for (let i = 0; i < length; i++) {
if (Object.prototype.toString.call(arr[i]) === "[object Array]") {
res.res.concat(flat(arr[i]));
} else {
res.push(arr[i]);
}
}
return res;
}
是否包含一个指定的值,返回boolean
Array.includes(valueToFind, fromIndex)
从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例
Array.from(arrayLike, mapFn)
4、Array.find
返回数组中满足提供的测试函数的第一个元素的值,否则返回undefined
Array.find(callback(element, index, array(数组本身)))
const target = {
foo: "bar",
baz: "qux"
};
const handler = {
get(trapTarget, property, receiver) {
let decoration = "";
if (property === "foo") {
decoration = "!!!";
}
return Reflect.get(...arguments) + decoration;
},
set(trapTarget, property, value, receiver) {
return Reflect.set(...arguments) + "set";
}
};
const proxy = new Proxy(target, handler);
proxy.foo = "good";
console.log(proxy.foo); // good!!!
console.log(target.foo); // good!!!
作者:白马不是马