赞
踩
let
引入了块级作用域的概念, 创建setTimeout
函数时,变量i在作用域内。对于循环的每个迭代,引用的i是i的不同实例。const
就很简单了, 在let
的基础上, 不可被修改
js
代码解读
- for(var i=0;i<=3;i++)
- {
- setTimeout(function()
- { console.log(i) }, 10);
- } //4~4
原因
:
var
定义的变量是全局的
, 所以全局只有一个变量i。setTimeout是异步
, 在下一轮事件循环, 等到执行的时候, 去找i变量的引用。所以函数找到了遍历完后的i, 此时它已经变成了4。js
代码解读
for(var i = 0; i <=3; i++) {
// 通过自执行函数满足0,1,2,3
(function (i)
{
setTimeout(function () { console.log(i); }, 10);
})(i);
} //0~3
for(let i=0;i<=3;i++)
{
//通过作用域 let 满足0,1,2,3
setTimeout(function() { console.log(i) }, 10);
}
//0~3,let为块级作用域
const teacher = {
name: 'zhangsan',
getName: function() { return `${this.name}` }
}
console.log(teacher.getName()); //zhangsan
const teacher = {
name: 'zhangsan',
getName: () => { return `${this.name}` }
}
console.log(teacher.getName()); //undefined
箭头函数不能被用作构造函数
构造函数: 改变this指向到新实例出来的对象.
箭头函数: this指向是定义的时候决定的.
const arrowFn = (value) => Number(value);
console.log(arrowFn('aaa'))
const arrowFn = () => {} console.log(arrowFn) //undefined
const arrowFn = () => ({}) //如果想得到对象,需要用括号包裹
console.log(arrowFn) //{}
js
代码解读
复制代码
class Test { _name = ''; constructor() { this.name = 'zhangsan'; } static getFormatName() { return `${this.name} - xixi`; } get name() { return this._name; } set name(val) { console.log('name setter'); this._name = val; } } console.log(new Test().name) //name setter zhangsan console.log(Test.getFormatName()) //name setter Test - xixi
支持变量和对象解析和换行
js
代码解读
复制代码
const b = 'test' const a = `${b} - xxxx`; const c = `我是换行 我换行了! `;
面试题:编写render函数, 实现template render功能.
js
代码解读
复制代码
//要求 const year = '2021'; 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) // 2021-10-01 //实现:高阶函数(函数返回函数) function render(template) { return function(context) { return template.replace(/${(.*?)}/g, (match, key) => context[key]); } } //.*表示:任意值 //?表示:匹配前面的表达式0或1个,或制定非贪婪限定符 //表达式 .* 就是单个字符匹配任意次,即贪婪匹配。 //表达式 .*? 是满足条件的情况只匹配一次,即最小匹配. //match: ${year} ${month} ${day} //key: year month day
参考资料:replace的mdn资料
replace() 方法返回一个由替换值(replacement)替换部分或所有的模式(pattern)匹配项后的新字符串。模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要调用的回调函数。如果pattern是字符串,则仅替换第一个匹配项。
js
代码解读
复制代码
str.replace(regexp|substr, newSubStr|function)
替换字符串可以插入下面的特殊变量名:
变量名 | 代表的值 |
---|---|
$$ | 插入一个 "$"。 |
$& | 插入匹配的子串。 |
'$ | 插入当前匹配的子串左边的内容。 |
$' | 插入当前匹配的子串右边的内容。 |
$n | 假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始。如果不存在第 n个分组,那么将会把匹配到到内容替换为字面量。比如不存在第3个分组,就会用“$3”替换匹配到的内容。 |
$<Name> | 这里Name 是一个分组名称。如果在正则表达式中并不存在分组(或者没有匹配),这个变量将被处理为空字符串。只有在支持命名分组捕获的浏览器中才能使用。 |
函数的参数:
变量名 | 代表的值 |
---|---|
match | 匹配的子串。(对应于上述的$&。) |
p1,p2, ... | 假如replace()方法的第一个参数是一个RegExp对象,则代表第n个括号匹配的字符串。(对应于上述的1,1,1,2等。)例如,如果是用 /(\a+)(\b+)/ 这个来匹配,p1 就是匹配的 \a+ ,p2 就是匹配的 \b+ 。 |
js
代码解读
复制代码
// 基础类型解构 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, d) // 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
js
代码解读
复制代码
// 对象属性解构 let { f1, f2 } = { f1: 'test1', f2: 'test2' } console.log(f1, f2) // test1, test2 // 可以不按照顺序,这是数组解构和对象解构的区别之一 let { f2, f1 } = { f1: 'test1', f2: 'test2' } console.log(f1, f2) // test1, test2 // 解构对象重命名 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接口,就能通过遍历操作,依次按顺序处理数据结构内所有的成员。使用for of的语法遍历数据结构时,自动寻找Iterator接口。
可迭代对象是Iterator接口的实现,有两个协议:可迭代协议和迭代器协议。
必须实现iterator方法
,即对象或其原型链上必须有一个名叫Symbol.iterator
的属性,该属性的值为无参函数,函数返回迭代器协议。必须实现next()方法
,方法返回对象有done
和value
属性。js
代码解读
复制代码
// 生成Iterator对象 function generateIterator(array) { let nextIndex = 0 return { next: () => nextIndex < array.length ? { value: array[nextIndex++], done: false } : { value: undefined, done: true } }; } const iterator = generateIterator([0, 1, 2]) console.log(iterator.next()) // {value: 0, done: false} console.log(iterator.next()) // {value: 1, done: false} console.log(iterator.next()) // {value: 2, done: false} console.log(iterator.next()) // {value: undefine, done: true}
缺点
:
Object.prototype
js
代码解读
复制代码
let obj = { 'a': 'test1', 'b': 'test2' } for (const key in obj){ console.log(key, obj[key]) //遍历数组时,key为数值下标字符串;遍历对象,key为对象字段名 }
js
代码解读
复制代码
let arr = [{age: 1}, {age: 5}, {age: 100}, {age: 34}] for(let {age} of arr) { if (age > 10) { break // for of 允许中断 } console.log(age) }
该方法返回一个给定对象的自身可枚举属性组成的数组。
js
代码解读
复制代码
const obj = { a: 1, b: 2 }; const keys = Object.keys(obj); // [a, b]
js
代码解读
复制代码
const obj = { a: 1, b: 2 }; const keys = Object.keys(obj); // [1, 2]
该方法返回一个给定对象自身可枚举属性的键值对数组。
js
代码解读
复制代码
const obj = { a: 1, b: 2 }; const keys = Object.entries(obj); // [ [ 'a', 1 ], [ 'b', 2 ] ]
该方法返回一个数组,该数组对元素是 obj自身拥有的枚举或不可枚举属性名称字符串。
js
代码解读
复制代码
Object.prototype.aa = '1111'; const testData = { a: 1, b: 2 } for (const key in testData) { console.log(key); } console.log(Object.getOwnPropertyNames(testData)) // ['a','b']
object包括属性和方法,其中属性分为数据属性和访问器属性
数据属性
访问器属性
默认值:
Object.defineProperty(obj, propertyName, descriptor) //用于对象的单个属性定义,参数:对象,属性名称,描述符对象
Object.defineProperties(obj, descriptor)//用于对象多个属性定义,参数:对象、描述符对象(属性与添加或修改的属性一一对应)
js
代码解读
复制代码
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不生效
js
代码解读
复制代码
const person = { isHuman: false, printIntroduction: function () { console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`); } }; const me = Object.create(person); me.name = "lubai"; me.isHuman = true; me.printIntroduction(); console.log(person); const myObject = Object.create(null)
传入第二个参数是怎么操作的呢?
js
代码解读
复制代码
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)的意义是什么呢? 平时创建一个对象Object.create({}) 或者 直接声明一个{} 不就够了?
Object.create(null)创建一个对象,但这个对象的原型链为null,即Fn.prototype = null
js
代码解读
复制代码
const b = Object.create(null) // 返回纯{}对象,无prototype b // {} b.__proto__ // undefined b.toString() // throw error
所以当你要创建一个非常干净的对象, 没有任何原型链上的属性, 那么就使用Object.create(null). for in 遍历的时候也不需要考虑原型链属性了.
js
代码解读
复制代码
let dest = {} let src = {id: 'src', a: {}} let result = Object.assign(dest, src) console.log(dest === result) //true console.log(result) //{id: 'src'} 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){}}
js
代码解读
复制代码
const a = { name: 1 }; const b = a; console.log(Object.is(a, b)) // true console.log(Object.is({}, {})) // false
返回指定对象的属性描述符对象
js
代码解读
复制代码
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, //可被for in遍历 configurable: false //删除属性或修改属性特性 } } */
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回
js
代码解读
复制代码
const arr1 = [1, 2, [3, 4]]; arr1.flat(); // [1, 2, 3, 4] const arr2 = [1, 2, [3, 4, [5, 6]]]; arr2.flat(); // [1, 2, 3, 4, [5, 6]] const arr3 = [1, 2, [3, 4, [5, 6]]]; arr3.flat(2); // [1, 2, 3, 4, 5, 6] //使用 Infinity,可展开任意深度的嵌套数组(无限大) const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]]; arr4.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Array.includes(valueToFind, fromIndex)
js
代码解读
复制代码
const array1 = [1, 2, 3]; console.log(array1.includes(2)); const pets = ['cat', 'dog', 'bat']; console.log(pets.includes('cat'));
其实它有两个参数, 只不过我们平时只使用一个.
valueToFind 需要查找的元素值。
fromIndex 可选 从fromIndex 索引处开始查找 valueToFind。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻)。默认为 0。
js
代码解读
复制代码
[1, 2, 3].includes(2); // true [1, 2, 3].includes(4); // false [1, 2, 3].includes(3, 3); // false [1, 2, 3].includes(3, -1); // true [1, 2, NaN].includes(NaN); // true // fromIndex 大于等于数组长度 var arr = ['a', 'b', 'c']; arr.includes('c', 3); // false arr.includes('c', 100); // false // 计算出的索引小于 0 var arr = ['a', 'b', 'c']; arr.includes('a', -100); // true arr.includes('b', -100); // true arr.includes('c', -100); // true
Array.find(callback(element, index, array(数组本身)))
callback 在数组每一项上执行的函数,接收 3 个参数:
js
代码解读
复制代码
const test = [ {name: 'lubai', age: 11 }, {name: 'xxx', age: 100 }, {name: 'nnn', age: 50} ]; function findLubai(teacher) { return teacher.name === 'lubai'; } console.log(test.find(findLubai));
4.1 Array.from() 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
4.2 Array.from() 可以通过以下方式来创建数组对象:
js
代码解读
复制代码
console.log(Array.from('foo')); console.log(Array.from([1, 2, 3], x => x + x)); const set = new Set(['foo', 'bar', 'baz', 'foo']); Array.from(set); // [ "foo", "bar", "baz" ] const map = new Map([[1, 2], [2, 4], [4, 8]]); Array.from(map); // [[1, 2], [2, 4], [4, 8]] const mapper = new Map([['1', 'a'], ['2', 'b']]); Array.from(mapper.values()); // ['a', 'b']; Array.from(mapper.keys()); // ['1', '2'];
所以数组去重我们可以怎么做?
js
代码解读
复制代码
function unique (arr) { return Array.from(new Set(arr)) // return [...new Set(arr)] } const test = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a']; console.log(unique(test)); function unique(arr) { const map = new Map(); const array = []; // 数组用于返回结果 for (let i = 0; i < arr.length; i++) { if (!map.has(arr[i])) { // 如果有该key值 array.push(arr[i]); map.set(arr[i], true); } } return array; } function unique(arr) { if (!Array.isArray(arr)) { console.log('type error!') return } const array = []; for (let i = 0; i < arr.length; i++) { if (!array.includes(arr[i])) { //includes 检测数组是否有某个值 array.push(arr[i]); } } return array }
Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。
js
代码解读
复制代码
Array.of(7); // [7] Array.of(1, 2, 3); // [1, 2, 3]
那怎么去模拟实现它呢?
js
代码解读
复制代码
Array.of = function() { return Array.prototype.slice.call(arguments); };
反射Reflect
代理Proxy
js
代码解读
复制代码
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
js
代码解读
复制代码
function generateIterator(array) { let nextIndex = 0 return { next: () => nextIndex < array.length ? { value: array[nextIndex++], done: false } : { value: undefined, done: true } }; } const iterator = generateIterator([0, 1, 2]) console.log(iterator.next()) // {value: 0, done: false} console.log(iterator.next()) // {value: 1, done: false} console.log(iterator.next()) // {value: 2, done: false} console.log(iterator.next()) // {value: undefine, done: true}
js
代码解读
复制代码
// 面试题: 实现一个可以for of遍历的对象 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 } } } } } } for (const item of obj) { console.log(item) }
或者
js
代码解读
复制代码
const iterable = { 0: 'a', 1: 'b', 2: 'c', length: 3, [Symbol.iterator]: Array.prototype[Symbol.iterator], }; for (const item of iterable) { console.log(item); }
js
代码解读
复制代码
function getObjectKeys(obj) { const result = []; for (const prop in obj) { if (obj.hasOwnProperty(prop)) { //注意 当使用for in遍历对象时,一定要进行hasOwnProperty判断,否则会遍历原型链上的值 result.push(prop); } } return result; } console.log(getObjectKeys({ a: 1, b: 2 }))
js
代码解读
复制代码
function getObjectValues(obj) { const result = []; for (const prop in obj) { if (obj.hasOwnProperty(prop)) { result.push(obj[prop]); } } return result; } console.log(getObjectValues({ a: 1, b: 2 }))
js
代码解读
复制代码
function getObjectEntries(obj) { const result = []; for (const prop in obj) { if (obj.hasOwnProperty(prop)) { result.push([prop, obj[prop]]); } } return result; } console.log(getObjectEntries({ a: 1, b: 2 }))
js
代码解读
复制代码
// 实现浅拷贝函数 function shallowClone(source) { const target = {}; for (const i in source) { if (source.hasOwnProperty(i)) { target[i] = source[i]; } } return target; } const a = { b: 1, c: { d: 111 } } const b = shallowClone(a); b.b = 2222; b.c.d = 333; console.log(b) // { b: 2222, c: { d: 333 } } console.log(a) // { b: 1, c: { d: 333 } }
js
代码解读
复制代码
// 使用 reduce、concat 和递归展开无限多层嵌套的数组 const arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]]; function flatDeep(arr, d = 1) { if (d > 0) { return arr.reduce((res, val) => { if (Array.isArray(val)) { res = res.concat(flatDeep(val, d - 1)) } else { res = res.concat(val); } return res; }, []) } else { return arr.slice() } }; console.log(flatDeep(arr1, Infinity)) // [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
如果不考虑深度, 咱们直接给他无限打平
js
代码解读
复制代码
function flatten(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(flatten(arr[i])) } else { res.push(arr[i]) } } return res } // 如果数组元素都是Number类型 function flatten(arr) { return arr.toString().split(',').map(item => +item) } function flatten(arr){ while(arr.some(item=>Array.isArray(item))){ arr = [].concat(...arr); } return arr; }
for in: 遍历数组输出索引,遍历对象输出key;
markdown
代码解读
复制代码
缺点: 1.不仅会遍历当前对象,还会遍历原型链上的属性; 2.不适合遍历数组
for of: 仅遍历当前对象
forEach 不会被break中断
作者:扬子鳄
链接:https://juejin.cn/post/7067840528246636575
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。