一天一个js知识

原型继承和class继承

class:js中并不存在类的概念,class只是语法糖,本质还是函数;

提升&暂时性死区

console.log(a)// ƒ a() {}
var a=8
function a(){}
复制代码

1、这里说明函数的提升要优先于变量的提升;函数提升会把整个函数挪到作用域顶部,变量提升只会把声明挪到作用域顶部

2、如果把函数代码删掉,把var变成let,则会报错a is not defined这时候的报错则是因为暂时性死区的原因导致,我们不能在声明前就使用变量;

疑问:为什要有提升这个东西? 存在原因:其实提升存在的根本原因就是为了解决函数间互相调用的情况

原型

每个对象当我们打印的时候下面都会有个__proto__属性,该属性指向了原型,原型也是一个对象,且这个对象包含了很多的函数,还有一个 constructor属性,也就是构造函数,打开 constructor 属性我们又可以发现其中还有一个 prototype 属性,并且这个属性对应的值和先前我们在 proto 中看到的一模一样。所以我们又可以得出一个结论:原型的 constructor 属性指向构造函数,构造函数又通过 prototype 属性指回原型,但是并不是所有函数都具有这个属性,Function.prototype.bind() 就没有这个属性。

总结: Object 是所有对象的爸爸,所有对象都可以通过 proto 找到它 Function 是所有函数的爸爸,所有函数都可以通过 proto 找到它 函数的 prototype 是一个对象 对象的 proto 属性指向原型, proto 将对象和原型连接起来组成了原型链

浅拷贝&&深拷贝

浅拷贝:

1、const obj=Object.assign(target,...sources)将sources对象的所有属性值拷贝到target对象上,实现的是浅拷贝,改变sources的属性值,obj不会发生改变

2、通过展开运算符 ... 来实现浅拷贝

let b={...a}
//这时改变a的属性值b也不会改变
复制代码

浅拷贝只解决了第一层的问题,如果目标对象的值中还有对象的话,那么就又回到最开始的话题了,两者享有相同的地址。要解决这个问题,我们就得使用深拷贝了。

深拷贝: 1、通过 JSON.parse(JSON.stringify(object)) 来实现深拷贝

let b = JSON.parse(JSON.stringify(a))
复制代码

但是此方法会忽略 undefined和symbol、不能序列化函数、不能解决循环引用的对象

2、使用new MessageChannel()

function structuralClone(obj) {
    return new Promise(resolve => {
    const { port1, port2 } = new MessageChannel()
    port2.onmessage = ev => resolve(ev.data)
    port1.postMessage(obj)
    })
}

var obj = {
  a: 1,
  b: {
    c: 2
  }
}

obj.b.d = obj.b

// 注意该方法是异步的
// 可以处理 undefined 和循环引用对象
const test = async () => {
  const clone = await structuralClone(obj)
  console.log(clone)
}
test()
复制代码

bind(),call(), apply()

前面讲到了this指向 那么这次就说说改变this指向的三个方法

bind方法是用于创建一个函数,使这个函数不论怎么调用都有同样的this,该方法返回一个新函数,你必须调用它才会生效,参数跟call一样第一个是this,参数是直接放进去的,第二第三第n个参数全都用逗号分隔,直接放到后面

call与bind的区别就是它返回的是一个执行结果不需要调用

apply它与call的区别是第二个参数接受的是一个数组

以上三个的参数都没有限制可以是各种类型

bind()函数内部怎么实现的:

Function.prototype.myBind=function(context){
    if(typeof this!=='function'){
        throw new TypeError('err')
    }
    const _this=this
    const args=[...arguments].slice(1)
    //返回一个函数
    return F(){
        //最后来说通过 new 的方式,在之前的章节中我们学习过如何判断 //this,对于 new 的情况来说,不会被任何方式改变 //this,所以对于这种情况我们需要忽略传入的 this
        if(this instanceof F){
            return new _this(...args,...arguments)
        }
        //对于直接调用来说,这里选择了 apply //的方式实现,但是对于参数需要注意以下情况:因为 bind //可以实现类似这样的代码 f.bind(obj, //1)(2),所以我们需要将两边的参数拼接起来,于是就有了这样的实现 //args.concat(...arguments)
        return _this.apply(context,args.concat(...arguments))
    }
}
复制代码

call()函数内部怎么实现的:

Function.prototype().myCall=function(context){
    if(typeoof this!=='function'){
        throw new TypeError('err')
    }
    context=context||window //context是可选参数,不传默认window
    context.fn=this //给context创建fn属性,并将值设置为需要调用的函数
    const args=[...arguments].slice(1)//因为call可以传入多个参数,需要将参数剥离出来
    const resule=context.fn(...args)//调用函数
    delete context.fn//调用完后删除对象上的函数
    return result
}
复制代码

apply()函数内部怎么实现的:

Function.prototype.myApply = function(context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  context = context || window
  context.fn = this
  let result
  // 处理参数和 call 有区别
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}
复制代码

this指向

对于this指向很多人都会混淆,其实很多网上把它说负复杂了看以下场景:

function foo(){
    console.log(this)
}
foo()
let num=1
const obj={
    num:1,
    foo:foo
}
obj.foo()
const c=new foo()
复制代码

传一张最近看的小册的老哥画的图,贼稳

instanceof

类似于typeof判断数据类型,因为起内部机制是通过原型链来判断的所以要更加严谨,但是对于原始类型想直接通过instanceof来判断是不行的,但是我们还是有办法让它判断原始类型的代码如下:

class   PrimitiveString{
    static [symbol.hasInstance(x){
        return typeof x=='string'
    }]
}
console.log('hello' instanceof PrimitiveString) //true
复制代码

typeof()

typeof()对于原始数据类型:number,string,boolean,symbol(es6引入的新的数据类型表示第一无二的值)除了null都可以显示正确类型,除了null,对于对象来说除了function都会返回object,所以此方法并不能正确返回数据类型

every()

用于检测数组中的所有元素是否都符合指定条件 every() 方法使用指定函数检测数组中的所有元素: 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。 如果所有元素都满足条件,则返回 true。 注意: every() 不会对空数组进行检测。 注意: every() 不会改变原始数组。 example1:数组中的元素是否满足大于等于4

let arr=[12,4,56,78,90]
let boolean=arr.every(item=>item>=4)//true
复制代码

map

map() 方法返回一个由原数组中的每个元素调用一个指定方法后的返回值组成的新数组。 传递给map()的函数的调用方式和传递给forEach()的函数的调用方式一样。但传递给map()的函数应该有返回值。注意:map()返回的是新数组:它不修改调用 的数组。

function square(arr){
    return arr.map(function(item){
        return item*item
     })
}
let arr=[1,2,3,4,5,6]
let arr2=square(arr)
console.log(arr2) //[1, 4, 9, 16, 25, 36]
复制代码

includes

与ES5中的indexOf类似,indexOf用来查找某个元素在数组中的位置有则返回元素位置索引,没有则返回-1,但是不能判断是否有NaN的元素;ES6中提供了Array.inclides()此方法只返回true和false即包含不包含,不能定位元素可判断NaN,可两个结合使用。 Array.inclides()的第二个参数表示判断的起始位置,若为负数表示从右起第几个但是不改变判断方向。

filter:

用于把Array的某些元素过滤掉返回剩下的元素,参数接受一个函数,函数作用于每一个元素,根据返回值是true或false决定保留还是丢弃该元素。filter是一个高阶函数关键在于正确实现一个筛选函数。 filter()接收的回调函数有三个,第一个是表示某个元素,第二个表示位置,第三个表示数组本身

example1:数组去重

let
    r,
    arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];
r = arr.filter(function (element, index, self) {
    return self.indexOf(element) === index;
});
复制代码

去除重复元素依靠的是indexOf总是返回第一个元素的位置,后续的重复元素位置与indexOf返回的位置不相等,因此被filter滤掉了。

example2:a数组中删除b数组中的元素

let a=[1,2,3,4,5,6],
    b=[1,2,3];
let c=a.filter(item=>!b.includes(item))
console.log(c) //[4,5,6]
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值