Open kangkai124 opened 5 years ago
数字转化为千分位格式
function toThousands(num) {
var num = (num || 0).toString(), result = '';
while (num.length > 3) {
result = ',' + num.slice(-3) + result;
num = num.slice(0, num.length - 3);
}
if (num) { result = num + result; }
return result;
}
const getItems = count =>
Array.from({ length: count }, (v, k) => k).map(k => ({
id: item-${k},
content: item ${k},
}));
上面没看懂,主要是 { length: count }
这里没看明白。
Array.from 方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
查了一下才知道,原来 { length: count } 是类数组对象,再加一个例子就看明白了,如下:
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
Array.from(arrayLike, (v, k) => v); // ["a", "b", "c"]
Array.from(arrayLike, (v, k) => k); // [0, 1, 2]
Object.defineProperty(obj, prop, descriptor)
Parameters
obj
The object on which to define the property.
prop
The name or Symbol
of the property to be defined or modified.
descriptor
The descriptor for the property being defined or modified.
Return value
The object that was passed to the function.
Property descriptors present in objects come in two main flavors: data descriptors and accessor descriptors. A data descriptor is a property that has a value, which may or may not be writable. An accessor descriptor is a property described by a getter-setter pair of functions. A descriptor must be one of these two flavors; it cannot be both.
configurable | enumerable | value | writable | get | set | |
---|---|---|---|---|---|---|
数据描述符 | Yes | Yes | Yes | Yes | No | No |
存取描述符 | Yes | Yes | No | No | Yes | Yes |
如果一个描述符不具有 value, writable, get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有( value 或 writable)和( get 或 set)关键字,将会产生一个异常。
Bear in mind that these attributes are not necessarily the descriptor's own properties. Inherited properties will be considered as well. In order to ensure these defaults are preserved, you might freeze the Object.prototype
upfront, specify all options explicitly, or point to null
with Object.create(null)
.
const name = '@kk/eslint-plugin-myself'
const matchs = name.match(/^(@[^/]+)\/eslint-plugin(?:-(.*))?$/)
console.log(matchs)
/*
[ '@kk/eslint-plugin-myself',
'@kk',
'myself',
index: 0,
input: '@kk/eslint-plugin-myself',
groups: undefined ]
*/
self 返回一个指向当前 window 对象的引用
if (window.parent.frames[0] != window.self) {
// this window is not the first frame in the list
}
检测当前环境下全局对象下的属性
function getGlobal(property) {
if (typeof self !== 'undefined' && self && property in self) {
return self[property];
}
if (typeof window !== 'undefined' && window && property in window) {
return window[property];
}
if (typeof global !== 'undefined' && global && property in global) {
return global[property];
}
if (typeof globalThis !== 'undefined' && globalThis) {
return globalThis[property];
}
};
reduce 应用
计算数组中每个元素出现的次数
var names = ['kk', 'mivi', 'dd', 'minghao', 'kk', 'mivi', 'kk', 'dd']
var countss = names.reduce((allNames, name) => {
if (name in allNames) {
allNames[name] = allNames[name] + 1
} else {
allNames[name] = 1
}
return allNames
}, {})
// {kk: 3, mivi: 2, dd: 2, minghao: 1}
按属性把对象分类
var people = [
{ name: 'kk', age: 25 },
{ name: 'mivi', age: 29 },
{ name: 'dd', age: 27 },
{ name: 'minghao', age: 26 },
{ name: 'kk', age: 25 },
{ name: 'nana', age: 24 }
]
var groupBy = (objectArray, property) => (
objectArray.reduce((acc, cur) => {
var key = cur[property]
if (!acc[key]) {
acc[key] = []
}
acc[key].push(cur)
return acc
}, {})
)
var ageGroup = groupBy(people, 'age')
数组去重
let arr = [1,2,1,2,3,5,4,5,3,4,4,4,4];
let result = arr.sort().reduce((init, current)=>{
if(init.length===0 || init[init.length-1]!==current){
init.push(current);
}
return init;
}, []);
console.log(result); //[1,2,3,4,5]
功能型函数管道
// Building-blocks to use for composition
const double = x => x + x;
const triple = x => 3 * x;
const quadruple = x => 4 * x;
// Function composition enabling pipe functionality
const pipe = (...functions) => input => functions.reduce(
(acc, fn) => fn(acc),
input
);
// Composed functions for multiplication of specific values
const multiply6 = pipe(double, triple);
const multiply9 = pipe(triple, triple);
const multiply16 = pipe(quadruple, quadruple);
const multiply24 = pipe(double, triple, quadruple);
// Usage
multiply6(6); // 36
multiply9(9); // 81
multiply16(16); // 256
multiply24(10); // 240
Object.prototype.hasOwnProperty()
hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性,该方法会忽略掉那些从原型链上继承到的属性。
Class 中的 super
1、Class 中的 super(),它在这里表示父类的构造函数,用来新建父类的 this 对象
super() 相当于 Parent.prototype.constructor.call(this)
class Demo {
constructor(x, y) {
this.x = x;
this.y = y;
}
customSplit() {
return [...this.y]
}
}
class Demo2 extends Demo {
constructor(x, y) {
super(x, y);
}
customSplit() {
return [...this.x]
}
task1() {
return super.customSplit();
}
task2() {
return this.customSplit();
}
}
let d = new Demo2('hello', 'world');
d.task1() //["w", "o", "r", "l", "d"]
d.task2() //["h", "e", "l", "l", "o"]
2、子类没有自己的 this 对象,而是继承父亲的 this 对象,然后进行加工。如果不调用 super,子类就得不到 this 对象
class Demo2 extends Demo{
constructor(x,y){
this.x = x; //this is not defined
}
}
ES5 的继承,实质上是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上(Parent.call(this)). ES6 的继承,需要先创建父类的 this,子类调用 super 继承父类的 this 对象,然后再加工。
如果子类没有创建 constructor,这个方法会被默认添加:
class Demo{
constructor(x) {
this.x = x;
}
}
class Demo2 extends Demo{}
let d = new Demo2('hello');
d.x // hello
3、super 在静态方法之中指向父类,在普通方法之中指向父类的原型对象
class Parent {
static myMethod(msg) {
console.log('static', msg);
}
myMethod(msg) {
console.log('instance', msg);
}
}
class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg);
}
myMethod(msg) {
super.myMethod(msg);
}
}
Child.myMethod(1); // static 1
var child = new Child();
child.myMethod(2); // instance 2
Promise.race()
Promise.race
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要 p1、p2、p3 之中有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。
Promise.race 方法的参数与 Promise.all 方法一样,如果不是 Promise 实例,就会先调用Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。
下面是一个例子,如果指定时间内没有获得结果,就将 Promise 的状态变为 reject,否则变为 resolve,用于 timeout 实现。
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
上面代码中,如果 5 秒之内 fetch 方法无法返回结果,变量 p 的状态就会变为 rejected,从而触发 catch 方法指定的回调函数。
URL & URLSearchParams
URL()
构造函数返回一个新创建的 URL对象 ,表示由参数定义的 URL。
var path = '/hello'
var url = new URL(path, 'http://api.com')
// url is as show as follows
// {
// hash: ""
// host: "api.com"
// hostname: "api.com"
// href: "http://api.com/hello"
// origin: "http://api.com"
// password: ""
// pathname: "/hello"
// port: ""
// protocol: "http:"
// search: ""
// searchParams: URLSearchParams {}
// username: ""
// }
console.log(url.toString())
// "http://api.com/hello"
URLSearchParam
是一个构造函数,处理 URL 的查询字符串
if (searchParams) {
const url = new URL(this._input, document && document.baseURI);
if (typeof searchParams === 'string' || (URLSearchParams && searchParams instanceof URLSearchParams)) {
url.search = searchParams;
} else if (Object.values(searchParams).every(param => typeof param === 'number' || typeof param === 'string')) {
url.search = new URLSearchParams(searchParams).toString();
} else {
throw new Error('The `searchParams` option must be either a string, `URLSearchParams` instance or an object with string and number values');
}
this._input = url.toString();
Object.is() 和 ===
不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
ES5 可以通过下面的代码,部署 Object.is()
。
Object.defineProperty(Object, 'is', {
value: function(x, y) {
if (x === y) {
// 针对+0 不等于 -0的情况
return x !== 0 || 1 / x === 1 / y;
}
// 针对NaN的情况
return x !== x && y !== y;
},
configurable: true,
enumerable: false,
writable: true
});
文件批量上传
async multipleUpload (toBeUploadList) {
let formData = new FormData()
toBeUploadList.forEach(t => {
formData.append('files', t.file, t.name)
})
try {
const res = await this.$post(`towerTasks/${this.$route.params.taskId}/batchUpload?fileKey=markov`, {
headers: null,
body: formData
})
if (res.code === 0) Message.success('马尔科夫文件上传成功')
} catch (error) {
Message.error('马尔科夫文件上传失败')
}
}
TypeError: Invalid attempt to spread non-iterable instance
这个错误是和变量解构有关,于是定位到了代码
formatData () {
const { headers, items } = this.data
return [headers, ...items]
}
this.data 初试值是 {}
,因此 items 是 undefined
,所以对 items 使用 延展符报了错。
解决方法如下:
formatData () {
const { headers, items } = this.data
if (headers && items) return [headers, ...items]
return []
}
对象循环引用
var a = { n: 1}
a.b = a;
这里 a
就是循环引用,那么如何判断呢?
其实 JSON.stringify()
就可以,JSON.stringify()
如果遇到参数里有循环引用的,就会抛出一个循环调用的错误 Uncaught TypeError: Converting circular structure to JSON。
其次的话,用递归也可以实现
function isCircular() {
let stack = []
return function fn(obj) {
let len = stack.length
for (; len--;) {
if (stack[len] === obj) {
throw new TypeError('There is circular structure')
}
}
stack.push(obj)
for (let k in obj) {
const value = obj[k]
if (typeof value === 'object') fn(value)
}
}
}
isCircular()(a)
每一种基本类型 Number、String、Boolean、Symbol 在对象中都有对应的类,所谓装箱转换,正是把基本类型转换为对应的对象,它是类型转换中一种相当重要的种类。
强制 Symbol 装箱:
var normal = Symbol("aa");
var symbolObject = (function(){ return this; }).call(Symbol("a"));
console.log(typeof normal); //symbol
console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true
// 当然,可以显式的调用装箱能力
var symbolObject2 = Object(Symbol('a'))
Object.prototype.toString 是可以准确识别对象对应的基本类型的方法,它比 instanceof 更加准确。
即对象类型到基本类型的转换。
对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。
拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。
到 Number 的拆箱,会先调用 valueOf 再调用 toString,到 String 的拆箱,则相反。
ES6 允许对象显式指定 Sumbol.toPrimitive 覆盖原有行为:
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"}
console.log(o + "")
// toPrimitive
// hello
指定了默认值以后,函数的length
属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length
属性将失真。
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
上面代码中,length
属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。比如,上面最后一个函数,定义了 3 个参数,其中有一个参数c
指定了默认值,因此length
属性等于3
减去1
,最后得到2
。
这是因为length
属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,后文的 rest 参数也不会计入length
属性。
(function(...args) {}).length // 0
如果设置了默认值的参数不是尾参数,那么length
属性也不再计入后面的参数了。
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
js 从入门到继续学习