JS知识巩固
全局类型检查
const isOfType = (() => {
const type = Object.create(null);//创建没有原型的对象
// 检查null类型
type.null = x => x === null;
// 检查undefined类型
type.undefined = x => x === undefined;
// 检查是否为undefined和null类型
type.nil = x => type.null(x) || type.undefined(x);
//检查字符串和字符串的文字类型。例如:'s', "s", `str`, new String()
type.string = x => !type.nil(x) && (typeof x === 'string' || x instanceof String);
// 检查数字或者数字文字类型。例如:12, 30.5, new Number()不包括NaN 和 Infinity
type.number = x => !type.nil(x)
&& (
(!isNaN(x) && isFinite(x)
&& typeof x === 'number'
) || x instanceof Number);
// 布尔类型检查 例如:true, false, new Boolean()
type.boolean = x => !type.nil(x) && (typeof x === 'boolean' || x instanceof Boolean);
// 检查数组类型
type.array = x => !type.nil(x) && Array.isArray(x);
// 对象类型检查 例如: {}, new Object(), Object.create(null)
type.object = x => ({}).toString.call(x) === '[object Object]';
// 检查提供的类型实例,参数一为实例对象,参数二为类型
type.type = (x, X) => !type.nil(x) && x instanceof X;
// 检查变量是否为Set类的实例
type.set = x => type.type(x, Set);
// 检查变量是否为Map类的实例
type.map = x => type.type(x, Map);
// 检查变量是否为Date类的实例
type.date = x => type.type(x, Date);
return type;
})();
检查对象里面的键
hasOwnProperty检查对象里面的键是否存在于对象且不在原型链上。
const obj = {
props: 'value'
}
console.log('props' in obj);//true
console.log('toString' in obj);//true
console.log(obj.hasOwnProperty('props'));//true
console.log(obj.hasOwnProperty('toString'));//false
数组去重Set
let arr = [1,2,2,1,2,3,3,1,5,8,48,9]
let arr1 = new Set(arr)
// 返回的是一个对象,需要用数组转化将其转化成数组
console.log(arr1);//Set(7) {1, 2, 3, 5, 8, …}
console.log(Array.from(arr1));//1, 2, 3, 5, 8, 48, 9]
// 但是当我们对对象类型的数组去重时,却得不到结果
let arrobj = [{id:1},{id:33},{id:33},{id:2},{id:33},{id:4},{id:56},{id:33},{id:33},{id:33}]
console.log(Array.from(new Set(arrobj)));
// 这时候我们就需要使用Set类的实例方法has和add
let idSet = new Set()
arrobj.forEach(item => {
let newId = idSet.has(item.id)//has方法是用来判断里面是否有这个值,返回的是布尔值
console.log(newId);
idSet.add(item.id)//add函数会检查里面是否有重复的值,没用的话才会添加
})
console.log(idSet);//Set(5) {1, 33, 2, 4, 56}
在数组遍历中使用break方法,使用some循环
我们执行数组遍历的时候一般都会使用forEach,在forEach遍历循环中我们可以实现continue的行为,只需要执行return就行。
let arr: Array<number> = [1,2,3,4,5,6,7,8,9]
arr.forEach(item => {
if(item === 3) {
return
}
console.log(item);
//1
//2
//4
//5
//6
//7
//8
//9
})
但是当我们需要实现break方法的时候forEach循环中无法实现,所以我们需要使用some循环实现
let arr: Array<number> = [1,2,3,4,5,6,7,8,9]
arr.some(item => {
if(item === 3) {
return false
}
if(item === 5) {
return true
}
console.log(item);
})
//打印1,2,4
//在some循环中实现continue只需要return false
//想要实现break只需要return true
使用别名和默认值
function ident1({dt: data}): void {
console.log(data);
}
ident1({
dt: {name: 'asda', id: 50}
})
//打印{name: 'asda', id: 50}
我们可以给我们传入的对象设置别名,并且通过别名访问它
我们还可以继续细化他
function ident2({dt: {name, id = 10}}): void {
console.log(name,id);
}
ident2({
dt: {name: 'samle'}
})
可选链和空值合并
const obj = {
data: {
container: {
name: {
value: '1230'
},
init: {
value: 0
}
}
}
}
// 一般我们查询是否存在某个值的时候会
console.log(
obj.data.container.init.value || 'have not init value'
);
// 可以看到打印的是have not init.value,但是其实是有值得所以我们需要换一种方法
console.log(
obj.data.container.init.value ?? 'have not init value'
);//打印得是 0
const obj = {
data: {
container: {
name: {
value: '1230'
},
init: {
value: 0
}
}
}
}
// console.log(
// obj.data.container.war.app.value ?? 'no value'
// );
//上述会报错,会阻断程序得继续运行,需要层层判断才行
console.log(
(obj && obj.data && obj.data.container && obj.data.container.war && obj.data.container.war.app && obj.data.container.war.app.value) || 'no value'
);//no value
// 我们可以看到我们写了一个十分长的判断条件,我们可以简化一下
console.log(
obj?.data?.container?.war?.app?.value || 'no value'
);
用函数拓展类
function Parent() {
const privateProp = 12;
var method = () => privateProp + 1
this.publicMethod = (x = 0) => method() + x
this.publicProps = 10
}
class Child extends Parent {
myProps = 20
}
let child = new Child()
console.log(child.publicMethod);//(x = 0) => method() + x
console.log(child.publicProps);//10
console.log(child.method);//undefined
console.log(child.privateProp);//undefined
可以看到在函数内部声明得都属于私有的实例属性,而使用this声明的都是可见的实例属性
拓展构造函数
先看call、apply、bind
var name = '小王',age = 17;
var obj = {
name: '小张',
objage: this.age,
myFun: function() {
console.log(this.name + this.age )
},
myFun1: function(city,to) {
console.log(this.name + this.age + city + to)
}
}
var newobj = {
name: '小李',
age: 99
}
obj.myFun()//小张undefined
obj.myFun.call()//指向了window,小王17
obj.myFun.apply()//指向了window,小王17
obj.myFun.bind()()//bind返回的是一个新的函数,需要执行,指向了window,小王17
obj.myFun.call(newobj)//指向了newobj。小李99
obj.myFun.apply(newobj)//指向了newobj,小李99
obj.myFun.bind(newobj)()//指向了newobj,小李99
// 第二个参数都为函数的传参
obj.myFun1.call(newobj,'珠海','深圳')//小李99珠海深圳
obj.myFun1.apply(newobj,['珠海','深圳'])//小李99珠海深圳,apply需要传入数组
obj.myFun1.bind(newobj,'珠海','深圳')()//小李99珠海深圳
obj.myFun1.bind(newobj,['珠海','深圳'])()//小李99珠海,深圳undefined 会解开数组
利用改变this的指向我们可以拓展构造函数
function func1() {
this.name1 = '123';
this.age = 123
}
function func2() {
this.name2 = ['123','321']
this.avgPerHour = '888'
}
function func3() {
this.name3 = 'Mr wang'
this.newobj = {};
func1.apply(this)
func2.apply(this.newobj)
}
let newvalue = new func3()
console.log(newvalue);
// func3 {name3: 'Mr wang', newobj: {…}, name1: '123', age: 123}
// age: 123
// name1: "123"
// name3: "Mr wang"
// newobj: {name2: Array(2), avgPerHour: '888'}