🍪🧁🍧

js中的原型

构造函数:

通过new一个函数来创建一个实例,那这个函数就是构造函数,箭头函数不能作为构造函数

prototype属性:

是函数特有的属性,让一个构造函数实例化的所有对象都有公共的方法和属性

_proto_属性:

是对象特有的属性,指向当前对象的原型对象
对象的_proto_属性就是他的构造函数的prototype属性

实现继承的几种方式
  • 原型链继承
function Parent() {
   this.name = "parent";
}
// 在父类的原型上定义方法
Parent.prototype.getName = function() {
   return this.name;
}
function Child() {
   this.name = "child";
}
// 子类继承父类,这里是关键,实现原型链继承
Child.prototype = new Parent();
// 实例化子类
var child1 = new Child();
console.log(child1.getName()); // 输出 "child"

缺点是包含引用类型值的原型属性会被所有实例共享。 换而言之,如果一个实例改变了该属性,那么其他实例的该属性也会被改变
引用类型值是指 JavaScript 中的对象、数组、函数等。它们的值存储在堆内存中,变量只是保存了指向堆内存的地址(引用)

  • 构造函数继承
function Parent() {
this.sayHello = function () {
    console.log("Hello");
};
}
Parent.prototype.a = "我是父类prototype上的属性";
// 子类
function Child() {
Parent.call(this);
}

// 创建两个 Child 实例
var child1 = new Child();
var child2 = new Child();

console.log(child1.sayHello === child2.sayHello); // 输出 false

采用复制父类构造函数上的属性和方法的方式来继承,但不能继承父类prototype上的属性和方法。
call()用于调用一个函数并显示指定函数内部的this值
常见应用场景包括:借用方法、构造函数继承、改变回调函数的 this

  • 组合继承
// 父类
function Parent() {
this.sayHello = function () {
    console.log("Hello");
};
}
Parent.prototype.a = "我是父类prototype上的属性"; 
// 子类
function Child() {
Parent.call(this);
}
Child.prototype = new Parent();
var child1 = new Child();

先call()再让子类的prototype继承父类prototype上的属性和方法
缺点是调用了两次Parent()

  • 寄生组合继承
    在组合继承的基础上,把Child.prototype = new Parent() 替换为Child.prototype = Object.create(Parent.prototype)
    Object.create(Parent.prototype) 创造了一个空对象,这个空对象的__proto__Parent.prototype 。所以它继承了 Parent原型链上的属性和方法。由于我们删除了Child.prototype = new Parent() 我们不再调用 Parent() 构造函数,因此 Child.prototype 不再包含 Parent 的属性和方法。
    缺点是Child()原有的prototype上的属性和方法会丢失

箭头函数、对象的方法、类的静态方法都没有prototype属性

const obj = {
    method: function() {}
};
console.log(obj.method.prototype); // undefined
class Example {
   staticMethod() {} //
}
console.log(Example.prototype.staticMethod.prototype); // undefined
class Example {
   static staticMethod() {} 
}
console.log(Example.staticMethod.prototype); // undefined
//手写instanceof
function instanceOf(example,classBasic){
    let classBasicPrototype=classBasic.prototype
    let examplePrototype=example._proto_
    while(true){
        if(classBasicPrototype==examplePrototype) return true
        if(!examplePrototype) return false
        examplePrototype=examplePrototype._proto_
    }
}
//手写类型检测,用typeof&Object.prototype.toString.call()实现
function myMethod(example){
    if(example==null) return 'null'
    if(typeof(example)!=='object'&&typeof(example)!=='function') {
        console.log('typeof')
        return typeof(example)
    }
    return Object.prototype.toString.call(example).toLowerCase().slice(8,-1)
}

js中的数据类型

  • 基本类型 number string boolean null undefined symbol bigint
  • 引用类型 Object Array Function Map Set...
    对于一个基本类型,在调用其方法时js会把他包装成一个包装类型,调用完销毁,但是不能添加属性

typeof

对象类型在内存中存储时机器码是000,null全是0,所以typeof会把null判为object

null&&undefined

Number(null)==0
Number(undefined)==NaN
//safeUndefined
let safeUndefined = void 0

isNaN

Number.isNaN()//不做类型转换,只有NaN返回true
isNaN()//做类型转换,转换不成NaN的话,返回true

数组索引

数字、对象、函数作为数组索引时会先被转换成字符串,Symbol不会

symbol

  • 默认的symbol对象两两不同
let id1=Symbol("我是id1")
let id2=Symbol("我是id1")
console.log(id1==id2) //false
console.log(id1.description) //我是id1
  • 创建两个相同的symbol对象
let id1=Symbol.for("我是id1")
let id2=Symbol.for("我是id1")
console.log(id1==id2) //true
console.log(Symbol.keyFor(id1)) //我是id1
  • symbol 作用
    • 隐藏对象属性
      在对象内的symbol属性通过key来遍历时是不可见的
let id=Symbol()
class student {
    constructor(name){
        this.name=name
        this[id]=666
    }
}
let sb=new student()
console.log(sb.name)
for (const key in sb) {console.log(key)}

遍历对象上的symbol属性

for (const key in Object.getOwnPropertySymbols(sb))
for (const key in Reflect.ownkeys(sb))

浅拷贝&&深拷贝

浅拷贝只拷贝一层

实现浅拷贝

  1. 对象的浅拷贝
const obj1={name:'lucy',age:18,tickets:{price:100,discount:0.8}}
let obj2=Object.assign({},obj1)
let obj3={...obj1}
let obj4={}
for(const key in obj1){//会遍历到原型链上的属性
	if(obj1.hasOwnProperty(key)){
		obj4[key]=obj1[key]
	}
}
Object.keys(obj1).forEach(()=>{
	obj4[key]=obj1[key]
})
  1. 数组的浅拷贝
arr1.slice()
[].concat(arr1)

实现深拷贝

const deepCopy=JSON.parse(JSON.stringify(obj))
  • 不会拷贝函数,JSON序列化会自动忽略函数
  • 不会拷贝特殊对象,如Date(),//输出字符串,regex//输出空对象
  • 不会拷贝原型链上的属性
  • 忽略undefined和Symbol()

手写深拷贝

//手写深拷贝
function deepClone(source,cloneMap=new Map()){
    if(typeof(source)!=="object"||source==null){
        return source
    } 
    if(cloneMap.has(source)){
        return cloneMap.get(source)
    }
    let target//解决无法拷贝Date()和Reg
    if(Array.isArray(typeof(source))){
        target=[]
    }else if(source instanceof Date){
        target=new Date()
    }else if(source instanceof RegExp){
        target=new RegExp(source.source,source.flags)
    }else {
        target={}
    }
    // const target=Array.isArray(source)?[]:{}
    cloneMap.set(source,target)//就是单独解决循环调用自身,这里只存了自己指向自己
    for (const key in source){
        if (typeof source[key]=="object" && source[key]!==null){
            target[key]=deepClone(source[key],cloneMap)
        }else {
            target[key]=source[key]
        }
    }
    //拷贝symbol
    const symbolKeys=Object.getOwnPropertySymbols(source)
    for(const symKey of symbolKeys){
        target[symkey]=deepClone(source[symKey],cloneMap)//symKey属性可能是引用类型
    }
    return target
}
  • source.source:获取正则表达式的主体字符串。

  • source.flags:获取正则表达式的修饰符(如 gim 等)。

为什么0.1+0.2大于0.3?

>0.1.toString(2)
'0.0001100110011001100110011001100110011001100110011001101'
>0.2.toString(2)
'0.001100110011001100110011001100110011001100110011001101'
>0.1+0.2
0.30000000000000004
>0.3.toString(2)
'0.010011001100110011001100110011001100110011001100110011'
>0.1+0.3
0.4

如何避免浮点数误差?

  1. 使用 toFixed()toPrecision() 进行舍入
    console.log((0.1 + 0.2).toFixed(1) == "0.3"); // true
    
  2. Math.round() 处理精度
    console.log(Math.round((0.1 + 0.2) * 10) / 10 === 0.3); // true
    
  3. 使用大整数计算(乘 10 或 100 再除回去)
    console.log((10 * 0.1 + 10 * 0.2) / 10 === 0.3); // true
    
  4. 使用 BigDecimal 库(如 decimal.js,适用于需要高精度计算的场景。

parseInt

parseInt(string,radix)返回一个整数
传入的第一个参数会被String()转换成字符串

String({})
'[object Object]'

类型转换

1. 加法

Object对象会先调用 ValueOf(),如果返回值不是基本类型,再调用toString

2. 减法||乘法

都转化成数字

posted @ 2025-03-12 12:07  不想吃fun  阅读(20)  评论(0)    收藏  举报