深拷贝,原型关系,this,bind函数,闭包

本文介绍了JavaScript中的值类型和引用类型,包括如何使用typeof和instanceof进行类型判断,深入讲解了深拷贝的实现,以及原型和构造函数的关系。同时,还探讨了this的引用场景、bind函数的实现原理以及异步和闭包的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

值类型和引用类型

值类型

  • 占用空间小
  • 有string、number、boolean、undefine以及es6的symbol
    值类型

引用类型

  • 占用空间大
  • 有Object、Array以及特殊引用类型null(特殊引用类型,不用于存储数据function)
  • 引用类型使用堆,key指向堆得内存地址
    引用类型

typeof判断类型

  • 所有值类型:string、number、boolean、undefine、symbol
  • 引用类型将array、object、null都归于object一类,能判断function
const fn = function () {}
const fnType = typeof fn   // function

instanceof判断类型

只能用来判断对象、函数和数组,不能用来判断字符串和数字
instanceof只能检测引用类型的,不能检测基本数据类型

a = {}  c = []   d = 'abc'  e = 123              
b = function() {} 

a instanceof Object     // true
b instanceof Object     // true
b instanceof Function   // true
c instanceof Array     // true
d instanceof String     // false
e instanceof Number     // false
null instanceof Object  // false

深拷贝代码

function deepClone( obj = {} ) {
  if (typeof obj !== 'object' || obj == null) {
    // obj是null,或者不是对象或数组,直接返回
    return obj
  }
  
  // 初始化返回结果
  let result
  if (obj instanceof Array) {
    result = []
  } else {
    result = {}
  }

  for (let key in obj) {
    if(obj.hasOwnProperty(key)) {
      // 递归调用
      result[key] = deepClone(obj[key])
    }
  }
  
  // 返回结果
  return result
}

运算符

==运算符

100 = '100' // true
0 = '' // true
0 = false // true
false = '' // true
null = undefined // true

以下是falsely变量,除此之外都是truly变量

!! 0 === false
!! NaN === false
!! '' === false
!! null === false
!! undefined === false
!! false === false

原型相关

// 通过类new 对象/实例
class Student {
  constructor(name, number) {
    this.name = name
    this.number = number
  }

  sayHi() {
    console.log(`姓名${this.name},学号${this.number}`)
  }
}

const xiaLuo = new Student('夏洛', 12)

// 父类
class People{
  contractor(name) {
    this.name = name
  }
  eat() {
    console.log(`${this.name}`)
  }
}

// 子类
class Student extends People {
  contractor(name,number) {
    super(name) // 调用父类的contractor(name)
    this.number = number
  }
  sayHi() {
    console.log(`姓名${this.name},学号${this.number}`)
  }
}
  • es6要求,子类构造函数必须执行一次super函数,否则在新建实例时会报错。
  • 如果不加super方法,子类就得不到this对象
  • 在子类的构造函数中,只有调用了super方法之后,才可以使用this关键字。这是因为子类实例的构建是基于对父类实例的加工,只有super方法才能返回父类的实例

原型关系

  • 每个class都有显示原型prototype
  • 每个实例都有隐式原型__proto__
  • 实例的__proto__指向对应class的prototype

原型的执行规则

  • 现在自身的属性和方法寻找
  • 如果找不到则自动去__proto__中查找
    原型
    原型链
    原型链

构造函数、实例、原型对象三者之间的关系

Star.prototype = {
    constructor: Star,
    sing: function() {
        console.log('我会唱歌')
    }
    movie: function() {
        console.log('我会看电影')
    }
}
const ldh = new Star()
  • 只要是对象就__proto__原型,指向原型对象
  • Star原型对象里面的__proto__原型指向的就是Object.prototype
  • Object.prototype原型对象里面的__proto__原型,指向为null
    在这里插入图片描述
    在这里插入图片描述

原型对象指向问题

  • 在构造函数中,里面的this指向的是对象实例 ldh
  • 原型对象里面的this指向的是实例对象ldh (函数的调用者)

手写jquery

class jQuery  {
  contractor(selector) {
    const result = document.querySelectorAll(selector) 
    const length = result.length
    for(let i = 0; i < length; i++) {
      this[i] = result[i] 
    }
    this.length = length
  }
  get(index) {
    return this[index]
  }
  each(fn) {
    for (let i = 0; i < length; i++) {
      const elem = this[i]
      fn(elem)
    }
  }
  on(type, fn) {
    return this.each(elem => {
      elem.addEventListener(type, fn, false)
    })
  }
}

const $p = new jQuery('p')
$p.get(1)
$p.each((elem) => console.log(elem.nodeName))
$p.on('click', ()=> alert('clicked'))
}

class myJquery extends jQuery {
  contractor(selector) {
    super(selector)
  }
  // 扩展自己的方法
  addClassName(className) {}
}

作用域

this

  • 作为普通函数
  • 使用call apply bind
  • 作为对象方法被调用
  • 在class方法中调用
  • 箭头函数

this不同引用场景如何取值

// this取值是在函数的时候定义的,不是在函数定义的时候确认的
// bind返回一个函数,需要重新执行函数
function fn1() {
  console.log(this)
}
fn1() // window

fn1.call({x: 100}) // {x: 100}

const fn2 = fn1.bind({x: 200})
fn2() // {x: 200}

箭头函数取得是上级作用域的this
this

call/apply/bind

相同点:三个函数都会改变this的指向
不同点:

  • bind会产生新函数
  • call和apply不会产生新函数,只是在调用的时候绑定一下
  • call和apply的区别,第一个参数都是表示要改变指向的那个对象,apply第二个参数是数组,call是将参数列举出来
// call、apply
function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}
const person = { name: 'Alice' };
greet.call(person, 'Hello', '!'); // 输出: "Hello, Alice!"
greet.apply(person, ['Hi', '!!']); // 输出: "Hi, Alice!!"
// bind
function greet() {
  console.log('Hello, ' + this.name);
}
const person = { name: 'Charlie' };
const greetPerson = greet.bind(person);
greetPerson(); // 输出: "Hello, Charlie"

call() 、apply()可以看作是某个对象的方法,通过调用方法的形式来间接调用函数(改变函数内部this的指向)。
bind() 就是将某个函数绑定到某个对象上。
call()在第一个参数之后的 后续所有参数就是传入该函数的值。就是说要全部列举出来。
apply() 只有两个参数,第一个是对象,第二个是数组,这个数组就是该函数的参数。
this 指向他们的第一个参数
bind()–也是改变函数体内this的指向;
bind 会创建一个新函数,称为绑定函数,当调用这个函数的时候,绑定函数会以创建它时传
入bind()方法的第一个参数作为this,传入bind()方法的第二个及以后的参数加上绑定
函数运行时本身的参数按照顺序作为原函数的参数来调用原函数;
bind 与 apply、call 最大的区别就是:bind不会立即调用,其他两个会立即调用

手写bind函数

Bind函数创建一个新的函数,在bind被调用时,这个新函数的this被指定为bind的第一个参数,而其余参数将作为新函数的参数,供调用时使用

Function.prototype.bind1 = function() {
  // 将参数拆解为数组 Arguments可以获取所有的参数 
  const args = Array.prototype.slice.call(arguments) 
  const t = args.shift() // 获取this(数组第一项) 
  const self = this  // fn1.bind(...)中的fn1 
  return function() { // 返回一个函数
    return self.apply(t, args)
  }
}

chatGTP

if (!Function.prototype.myBind) {
    Function.prototype.myBind = function (context, ...argsPrepend) {
        const self = this; // 保存原函数引用
        let args = [...argsPrepend]; // 保存预置参数

        // 返回一个新的函数,该函数在调用时会设置正确的上下文并传递预置参数
        return function (...argsAppend) {
            // 构建最终调用原函数时的参数列表,预置参数在前,调用时传入的参数在后
            const allArgs = [...args, ...argsAppend];

            // 确保在严格模式下也能正确设置this
            const fnContext = Object(self);
            const boundFn = function () {
                return self.apply(this instanceof boundFn ? this : context, allArgs);
            };

            // 继承原函数的原型
            if (typeof self.prototype !== 'undefined') {
                boundFn.prototype = Object.create(self.prototype);
            }

            return boundFn;
        };
    };
}

// 示例使用
function MyClass() {
    this.name = 'Original Class';
}

MyClass.prototype.sayHello = function (firstName, lastName) {
    console.log(`Hello ${firstName} ${lastName}, I'm ${this.name}`);
};

const obj = { name: 'Bound Object' };
const boundSayHello = MyClass.prototype.sayHello.myBind(obj, 'John');
boundSayHello('Doe'); // 输出 "Hello John Doe, I'm Bound Object"

异步

  • 异步解决单线程等待问题
  • 异步应用场景:网络请求,定时任务

闭包

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。
常用于计时器、私有变量、节流防抖

// 计时器
function createCounter() {
  let count = 0; // count 被闭包“记住”

  return function() {
    count++; // 内部函数访问外部函数的变量
    return count;
  };
}

// 使用闭包
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2 (count 状态被保留)
// 私有变量
function createPerson(name) {
  let privateAge = 0; // 外部无法直接访问

  return {
    getName: () => name,
    getAge: () => privateAge,
    setAge: (age) => { privateAge = age; }
  };
}

const person = createPerson("Alice");
person.setAge(25);
console.log(person.getName()); // "Alice"
console.log(person.getAge()); // 25
// person.privateAge; // 报错(无法直接访问)
// 节流
function throttle(fn, interval) {
  let lastTime = 0; // 闭包保存上次执行时间
  return function(...args) {
    const now = Date.now();
    if (now - lastTime >= interval) {
      fn.apply(this, args);
      lastTime = now; // 更新最后执行时间
    }
  };
}

// 使用示例
window.addEventListener('scroll', throttle(function() {
  console.log('滚动处理'); // 每隔 interval 毫秒执行一次
}, 1000));
// 防抖
function debounce(fn, delay) {
  let timer = null; // 通过闭包保存定时器
  return function(...args) {
    clearTimeout(timer); // 清除之前的定时器
    timer = setTimeout(() => {
      fn.apply(this, args); // 延迟执行
    }, delay);
  };
}

// 使用示例
const input = document.querySelector('input');
input.addEventListener('input', debounce(function() {
  console.log('搜索:', this.value); // 输入停止 delay 毫秒后执行
}, 500));
// 闭包隐藏数据,只提供api
function createCache() {
  const data = {}
  return {
    set: function(key, val) {
      data[key] = val
    }
    get: function(key) {
      return daka[key]
    }
  }
}

const c = createCache()
c.set('a', 100)
console.log(c.get('a')) // 100

闭包:自由变量的查找,是在函数定义的地方向上级作用域查找,不是在执行的地方

function print(fn) {
  const a =200
  fn()
}
const a = 100
function fn() {
  console.log(a)
}
print(fn) // 100
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值