当前位置: 首页 > news >正文

2025年01月09日德美医疗前端面试

目录

  1. vue2 的双向绑定的原理
  2. vue3 的双向绑定原理
  3. vue 的生命周期
  4. vue 子组件为何不能修改父组件的值
  5. js delete 删除数组的某一个值会怎么样
  6. vue 和 react 的 diff 算法
  7. 什么是闭包
  8. 原型链
  9. this指向

vue2 的双向绑定的原理

以下是 Vue 2 双向绑定的原理:

1. 核心概念

Vue 2 的双向绑定是通过数据劫持和发布订阅模式实现的。它允许数据和视图之间的双向通信,即数据的改变可以更新视图,视图的操作也可以更新数据。

2. 实现步骤

数据劫持

Vue 2 使用 Object.defineProperty 对数据对象的属性进行劫持,在属性被访问或修改时添加自定义的行为。

function defineReactive(obj, key, value) {let dep = new Dep(); // 创建一个空的订阅者列表Object.defineProperty(obj, key, {enumerable: true, // 允许属性被枚举configurable: true, // 允许属性被修改get: function() {if (Dep.target) {dep.addSub(Dep.target);}return value;},set: function(newVal) {if (value!== newVal) {value = newVal;dep.notify();}}});
}

在这个函数中:

  • defineReactive 函数使用 Object.definePropertyobjkey 属性进行定义。
  • get 方法:当该属性被访问时,如果 Dep.target 存在(在 Vue 中,Dep.target 通常是当前正在编译的 Watcher),将其添加到 dep 的订阅者列表中。
  • set 方法:当该属性被修改时,如果新值与旧值不同,更新属性值并通知 dep 的订阅者列表中的所有订阅者。

依赖收集

每个组件实例都有一个 Watcher 对象,它是一个订阅者,当组件的模板中使用到某个数据时,会触发该数据的 get 方法,将该 Watcher 添加到该数据的订阅者列表中。

class Watcher {constructor(vm, exp, cb) {this.vm = vm;this.exp = exp;this.cb = cb;this.value = this.get();}get() {Dep.target = this;let value = this.vm[this.exp];Dep.target = null;return value;}update() {let newValue = this.vm[this.exp];let oldValue = this.value;if (newValue!== oldValue) {this.value = newValue;this.cb.call(this.vm, newValue, oldValue);}}
}

在这个类中:

  • Watcherget 方法会将自己设置为 Dep.target,并访问数据,触发数据的 get 方法,从而将自己添加到该数据的订阅者列表中。
  • update 方法会在数据更新时被调用,调用回调函数 cb 更新视图。

发布订阅模式

Dep 类是一个简单的发布者,它维护一个订阅者列表,并在数据变化时通知订阅者。

class Dep {constructor() {this.subs = [];}addSub(sub) {this.subs.push(sub);}removeSub(sub) {this.subs = this.subs.filter(s => s!== sub);}notify() {this.subs.forEach(sub => sub.update());}
}

在这个类中:

  • constructor 方法创建一个空的订阅者列表。
  • addSub(sub) 方法添加一个订阅者。
  • removeSub(sub) 方法移除一个订阅者。
  • notify() 方法通知所有订阅者更新。
3. 整体流程
  • 当 Vue 实例化时,会对 data 属性中的数据进行遍历,使用 defineReactive 进行数据劫持。
  • 当编译模板时,会创建 Watcher 对象,在使用数据时触发数据的 get 方法,将 Watcher 添加到该数据的订阅者列表中。
  • 当数据发生变化时,触发数据的 set 方法,通知 Watcher 进行更新,Watcher 调用 update 方法更新视图。
4. 示例代码
<div id="app"><input v-model="message">{{ message }}
</div>
function Vue(options) {this.data = options.data;observe(this.data);new Compile('#app', this);
}function observe(data) {if (!data || typeof data!== 'object') return;Object.keys(data).forEach(key => {defineReactive(data, key, data[key]);});
}function Compile(el, vm) {this.vm = vm;this.el = document.querySelector(el);this.compileElement(this.el);
}Compile.prototype.compileElement = function (el) {let childNodes = el.childNodes;Array.from(childNodes).forEach(node => {if (node.nodeType === 1) {// 元素节点this.compileElement(node);} else if (node.nodeType === 3) {// 文本节点this.compileText(node);}});
};Compile.prototype.compileText = function (node) {let reg = /\{\{(.*?)\}\}/g;let value = node.textContent;if (reg.test(value)) {let exp = RegExp.$1.trim();node.textContent = value.replace(reg, this.vm.data[exp]);new Watcher(this.vm, exp, function (newVal) {node.textContent = value.replace(reg, newVal);});}
};let app = new Vue({data: {message: 'Hello, Vue!'}
});
代码解释

Vue 函数

  • 接收 options,初始化 data 属性,并调用 observe 对数据进行观察。
  • 调用 Compile 进行模板编译。

observe 函数

  • 遍历 data 对象,对每个属性使用 defineReactive 进行数据劫持。

Compile

  • 编译模板元素,对于文本节点,如果存在 {{...}} 插值表达式,使用 Watcher 进行数据监听和更新。

Watcher

  • 在实例化时,会将自己添加到数据的订阅者列表中,并在更新时更新视图。

Dep

  • 作为发布者,管理订阅者列表,在数据更新时通知订阅者。
面试回答示例

“Vue 2 的双向绑定是通过数据劫持和发布订阅模式实现的。首先,使用 Object.defineProperty 对数据对象的属性进行劫持,在属性的 get 方法中进行依赖收集,将使用该数据的 Watcher 订阅者添加到该数据的订阅者列表中,在 set 方法中,当数据发生变化时通知订阅者列表中的 WatcherWatcher 是一个订阅者,负责更新视图,它会在实例化时将自己添加到数据的订阅者列表中,并在更新时调用回调函数更新视图。Dep 类是一个发布者,负责维护订阅者列表并通知订阅者更新。当 Vue 实例化时,会对 data 中的数据进行劫持,在模板编译时,会创建 Watcher 进行依赖收集,从而实现数据和视图的双向绑定。这样,当数据变化时,视图会更新;当用户操作视图(如通过 v-model)时,会触发数据的更新,形成双向绑定的效果。”

通过这样的解释,可以向面试官展示你对 Vue 2 双向绑定原理的深入理解,包括数据劫持、依赖收集、发布订阅模式的使用以及整体的工作流程。

2. vue3 的双向绑定原理

Vue 3 的双向绑定机制相比 Vue 2 有了显著改进,主要通过 组合式 APIProxy 实现。以下是完整解析:

一、核心机制演进
特性Vue 2Vue 3
响应式基础Object.definePropertyProxy
检测范围对象属性完整对象
数组检测需特殊方法原生支持
性能递归转换属性惰性代理
二、响应式系统核心实现
1. reactive() - 对象响应化
function reactive(target) {return new Proxy(target, {get(target, key, receiver) {track(target, key) // 依赖收集return Reflect.get(target, key, receiver)},set(target, key, value, receiver) {Reflect.set(target, key, value, receiver)trigger(target, key) // 触发更新return true}})
}
2. ref() - 原始值响应化
function ref(value) {const refObject = {get value() {track(refObject, 'value')return value},set value(newVal) {value = newValtrigger(refObject, 'value')}}return refObject
}
三、依赖收集与触发流程
  1. 依赖收集阶段

    组件渲染
    读取响应式数据
    触发getter
    将当前effect存入dep
  2. 触发更新阶段

    数据变更
    触发setter
    从dep取出effect
    执行effect重新渲染
四、v-model 双向绑定实现
组件示例:
<CustomInput v-model="searchText" /><!-- 等价于 -->
<CustomInput :modelValue="searchText"@update:modelValue="newValue => searchText = newValue"
/>
组件实现:
defineProps(['modelValue'])
defineEmits(['update:modelValue'])const emitUpdate = (e) => {emit('update:modelValue', e.target.value)
}
五、性能优化策略
  1. 编译时优化

    • 静态节点提升(Hoist Static)
    • 补丁标志(Patch Flags)
    • 树结构拍平(Tree Flattening)
  2. 响应式优化

    • 依赖关系缓存(effect缓存)
    • 批量异步更新(nextTick合并)
  3. 源码结构优化

    // 惰性代理示例
    function shallowReactive(obj) {const proxy = new Proxy(obj, handlers)// 不立即递归代理嵌套对象return proxy 
    }
    
六、与 Vue 2 的对比升级
  1. 数组处理改进

    // Vue 2 需要特殊处理
    this.$set(this.items, index, newValue)// Vue 3 直接操作
    state.items[index] = newValue // 自动触发更新
    
  2. 动态属性检测

    // Vue 2 无法检测新增属性
    this.$set(this.obj, 'newProp', value)// Vue 3 自动检测
    state.newProp = value // 自动响应
    
七、开发注意事项
  1. 响应式丢失场景

    // 解构会导致响应式丢失
    const { x, y } = reactive({ x: 1, y: 2 })// 正确做法
    const pos = reactive({ x: 1, y: 2 })
    const { x, y } = toRefs(pos)
    
  2. 性能敏感操作

    // 大数据量使用shallowRef/shallowReactive
    const bigList = shallowRef([])// 非响应式数据使用markRaw
    const foo = markRaw({ complex: object })
    

Vue 3 的双向绑定通过 Proxy 实现了更精细的依赖跟踪,配合编译时优化,在保证开发体验的同时提供了更好的运行时性能。理解其原理有助于编写更高效的 Vue 代码。

3. vue 的生命周期

一、Vue 2 和 Vue 3 生命周期对比
生命周期钩子对照表
Vue 2 选项式APIVue 3 组合式API触发时机描述
beforeCreate无直接对应实例初始化前,data/methods未初始化
createdsetup()实例创建完成,data/methods可用
beforeMountonBeforeMount挂载开始前,DOM尚未生成
mountedonMounted挂载完成,DOM已生成
beforeUpdateonBeforeUpdate数据变化导致DOM更新前
updatedonUpdated数据变化导致DOM更新后
beforeDestroyonBeforeUnmount实例销毁前(vue3改名更准确)
destroyedonUnmounted实例销毁后(vue3改名更准确)
activatedonActivatedkeep-alive组件激活时
deactivatedonDeactivatedkeep-alive组件停用时
errorCapturedonErrorCaptured捕获子孙组件错误时
二、生命周期完整流程图示
初始化事件和生命周期
beforeCreate
初始化注入和响应式
created
编译模板/生成render函数
beforeMount
创建VDOM并渲染
mounted
数据变化?
beforeUpdate
重新渲染VDOM和DOM
updated
是否调用销毁方法?
beforeUnmount
移除DOM和事件监听
unmounted
三、组合式API中的使用示例
import { onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted 
} from 'vue'export default {setup() {onBeforeMount(() => {console.log('挂载前')})onMounted(() => {console.log('挂载完成')})onBeforeUpdate(() => {console.log('更新前')})onUpdated(() => {console.log('更新完成')})onBeforeUnmount(() => {console.log('销毁前')})onUnmounted(() => {console.log('销毁完成')})}
}
四、关键生命周期详解
1. created / setup
  • 数据访问:可访问响应式data、computed等
  • 异步请求:适合在此发起初始数据请求
  • 注意:此时DOM未生成,不可操作DOM
2. mounted
  • DOM操作:可安全操作DOM元素
  • 子组件:保证所有子组件也已挂载
  • 典型用途:初始化第三方库(如图表库)
3. updated
  • DOM状态:可获取更新后的DOM状态
  • 避免操作:不要在此修改状态,可能导致无限循环
  • 性能优化:复杂操作建议使用watch替代
4. unmounted
  • 清理工作:移除事件监听、定时器、取消网络请求等
  • 第三方库:销毁插件实例(如销毁ECharts实例)
五、特殊场景生命周期
Keep-alive组件
onActivated(() => {console.log('组件被激活')
})onDeactivated(() => {console.log('组件被缓存')
})
错误捕获
onErrorCaptured((err, instance, info) => {console.error('捕获到错误:', err)return false // 阻止错误继续向上传播
})
六、最佳实践建议
  1. 异步请求

    • 初始化数据:created/setup + async/await
    • 避免在mounted中请求,会延迟首次渲染
  2. DOM操作

    • 必须在mounted及之后生命周期进行
    • 使用nextTick确保DOM更新完成
  3. 内存管理

    onUnmounted(() => {clearInterval(timer)eventBus.off('eventName', handler)chartInstance.dispose()
    })
    
  4. 性能优化

    • 避免在updated中进行复杂计算
    • 大量数据更新考虑使用虚拟滚动

理解Vue生命周期有助于在正确时机执行代码,避免常见错误,并优化应用性能。Vue 3的组合式API提供了更灵活的生命周期管理方式。

4. vue 子组件为何不能修改父组件的值

一、核心原因:单向数据流原则

Vue 强制实施单向数据流(One-Way Data Flow)设计模式,这是现代前端框架的通用规范:

  1. 数据所有权明确:父组件拥有数据,子组件只有使用权
  2. 可预测性:数据变更源头唯一,便于追踪状态变化
  3. 维护性:避免多组件同时修改导致的混乱
二、直接修改的危害

如果允许子组件直接修改父组件值:

问题类型具体表现
状态混乱多个子组件同时修改同一状态,难以确定最终值
调试困难数据变更来源不明确,错误难以追踪
组件耦合子组件必须了解父组件内部实现,破坏组件独立性
性能优化障碍Vue的响应式系统难以优化变更检测
三、Vue 的解决方案
1. Props + Events 标准模式
<!-- 父组件 -->
<template><Child :value="parentValue" @update="handleUpdate" />
</template><script>
export default {data() {return { parentValue: 1 }},methods: {handleUpdate(newVal) {this.parentValue = newVal}}
}
</script><!-- 子组件 -->
<template><button @click="$emit('update', value + 1)">+1</button>
</template><script>
export default {props: ['value']
}
</script>
2. v-model 语法糖(Vue 2)
<!-- 父组件 -->
<Child v-model="parentValue" /><!-- 等价于 -->
<Child :value="parentValue" @input="parentValue = $event" />
3. v-model 参数(Vue 3)
<!-- 父组件 -->
<Child v-model:title="pageTitle" /><!-- 子组件 -->
<script setup>
defineProps(['title'])
defineEmits(['update:title'])
</script>
4. .sync 修饰符(Vue 2)
<!-- 父组件 -->
<Child :value.sync="parentValue" /><!-- 子组件 -->
this.$emit('update:value', newValue)
四、特殊情况的处理方案
1. 需要直接修改的情况
// 子组件
props: {value: {type: Object,default: () => ({})}
},
methods: {modifyParent() {const newObj = JSON.parse(JSON.stringify(this.value))newObj.property = 'new value'this.$emit('update', newObj)}
}
2. 使用 Vuex/Pinia 状态管理
// store
export const useStore = defineStore('main', {state: () => ({ count: 0 }),actions: {increment() {this.count++}}
})// 任何组件
import { useStore } from './store'
const store = useStore()
store.increment() // 通过集中式管理修改状态
五、底层原理分析

Vue 通过以下机制阻止直接修改:

  1. Prop 代理:Vue 在子组件实例上创建的 props 是只读代理

    // Vue 内部实现简化
    const childProps = {}
    Object.defineProperty(childProps, 'value', {get() { return parentValue },set() { if (process.env.NODE_ENV !== 'production') {warn(`Avoid mutating prop directly`)}}
    })
    
  2. 开发环境警告:在非生产环境下,Vue 会检测并警告 props 的直接修改

六、最佳实践建议
  1. 严格遵循单向数据流

    • 父级通过 props 向下传递数据
    • 子级通过事件向上通知变更意图
  2. 复杂场景处理方案

    场景解决方案
    需要修改父级对象属性触发事件让父级自己修改
    多层级组件通信使用provide/inject
    全局状态使用Vuex/Pinia
    临时本地修改使用computed或ref拷贝prop值
  3. 代码规范检查

    // ESLint规则推荐
    "vue/no-mutating-props": "error"
    

理解并遵守这一设计原则,可以构建出更健壮、可维护的Vue应用架构。

5. js delete 删除数组的某一个值会怎么样

一、基本行为表现

当使用 delete 操作符删除数组元素时:

const arr = ['a', 'b', 'c', 'd'];
delete arr[1];  // 删除索引1的元素'b'console.log(arr);        // ['a', empty, 'c', 'd']
console.log(arr.length); // 4
console.log(arr[1]);     // undefined
二、关键特性解析
1. 不会改变数组长度
  • delete 只会将指定位置的元素变为 empty 空位(稀疏数组)
  • 数组的 length 属性保持不变
2. 元素访问结果
  • 被删除的位置会返回 undefined
  • 但该位置仍在数组中(表现为 empty 而非 undefined)
console.log(1 in arr); // false (表示索引1不存在)
console.log(arr.hasOwnProperty(1)); // false
3. 遍历行为差异

不同遍历方法对空位的处理:

方法处理方式示例结果
for循环会处理空位(值为undefined)‘a’, undefined, ‘c’, ‘d’
forEach跳过空位‘a’, ‘c’, ‘d’
map跳过空位[‘a’, empty, ‘c’, ‘d’]
filter移除空位[‘a’, ‘c’, ‘d’]
三、与 splice 方法对比
操作delete arr[i]arr.splice(i, 1)
数组长度不变减少
空位产生不会
索引重排不重排后续元素前移
适用场景需要保留位置/长度时需要真正移除元素时
四、实际应用建议
1. 应该使用 delete 的场景
  • 需要保持数组长度不变(如游戏地图格子)
  • 需要保留元素位置信息(如时间序列数据)
2. 不应该使用 delete 的场景
  • 需要真正移除元素时(应改用 splice
  • 需要保证数组连续性的操作前(如 JSON.stringify 会忽略空位)
3. 正确的元素删除方法
// 方法1:splice (修改原数组)
arr.splice(index, 1);// 方法2:filter (创建新数组)
const newArr = arr.filter((_, i) => i !== index);// 方法3:设置length (截断数组)
arr.length = newLength;
五、特殊注意事项
  1. 类型化数组

    const typedArray = new Uint8Array([1, 2, 3]);
    delete typedArray[1]; // 会将索引1位置设为0
    
  2. 性能考虑

    • delete 操作比 splice 快(不涉及元素移动)
    • 但后续操作空位数组可能更慢
  3. Vue/React 响应式

    • 在响应式框架中,delete 可能不会触发视图更新
    • 应使用框架提供的删除方法(如 Vue 的 $delete
六、底层原理

delete 操作符实际执行的是:

  1. 将指定属性的 [[Configurable]] 特性设为 true
  2. 删除该属性
  3. 返回 true(即使属性不存在)

对于数组:

  • 数组是特殊对象,索引是属性名
  • delete arr[i] 等同于删除对象的属性

理解 delete 对数组的这种特殊行为,有助于避免在需要真正移除元素时错误使用它。大多数情况下,splicefilter 才是更合适的选择。

6. vue 和 react 的 diff 算法

Vue和React作为流行的前端框架,都使用虚拟DOM(Virtual DOM)来提升渲染性能,而Diff算法是虚拟DOM的核心,它能找出新旧虚拟DOM之间的差异,从而只更新需要更新的真实DOM部分。下面为你分别介绍它们的Diff算法。

Vue的Diff算法

Vue的Diff算法采用了双指针和key的策略,通过比较新旧虚拟节点的差异,最小化DOM操作。具体步骤如下:

  1. 同级比较:只对同一层级的节点进行比较。
  2. 节点类型比较:若节点类型不同,直接替换。
  3. key比较:若有key,使用key进行更高效的比较和复用。
  4. 双指针遍历:使用首尾双指针遍历新旧节点列表。
React的Diff算法

React的Diff算法基于几个启发式策略,旨在减少比较次数,提高性能。具体步骤如下:

  1. 同级比较:和Vue一样,只比较同一层级的节点。
  2. 节点类型比较:节点类型不同时,直接替换。
  3. key比较:使用key来标识列表中的元素,便于复用和移动。
  4. 列表比较:采用双循环遍历新旧节点列表。
代码示例

以下是一个简单的Vue和React组件示例,来帮助你理解Diff算法的应用。

Vue示例
<template><div><ul><li v-for="item in list" :key="item.id">{{ item.name }}</li></ul><button @click="updateList">Update List</button></div>
</template><script>
export default {data() {return {list: [{ id: 1, name: 'Item 1' },{ id: 2, name: 'Item 2' },{ id: 3, name: 'Item 3' }]};},methods: {updateList() {this.list = [{ id: 1, name: 'Updated Item 1' },{ id: 2, name: 'Updated Item 2' },{ id: 4, name: 'New Item 4' }];}}
};
</script>
React示例
import React, { useState } from 'react';const App = () => {const [list, setList] = useState([{ id: 1, name: 'Item 1' },{ id: 2, name: 'Item 2' },{ id: 3, name: 'Item 3' }]);const updateList = () => {setList([{ id: 1, name: 'Updated Item 1' },{ id: 2, name: 'Updated Item 2' },{ id: 4, name: 'New Item 4' }]);};return (<div><ul>{list.map(item => (<li key={item.id}>{item.name}</li>))}</ul><button onClick={updateList}>Update List</button></div>);
};export default App;
总结
  • Vue:采用双指针和key策略,能更精准地找出差异,更新DOM。
  • React:基于启发式策略,通过双循环遍历列表,性能也较为出色。

两者都运用了虚拟DOM和Diff算法,减少了不必要的DOM操作,提高了渲染性能。在实际开发中,合理使用key能进一步优化Diff算法的性能。

7. 什么是闭包

在 JavaScript 里,闭包是一个强大且重要的概念。下面为你详细解释 JavaScript 中的闭包。

定义

闭包是指有权访问另一个函数作用域中变量的函数。简单来说,即使外部函数执行完毕,其作用域内的变量也不会被销毁,而是会被闭包“捕获”并保留,使得这些变量能在外部函数之外被访问和修改。

形成条件

闭包的形成需要满足以下两个关键条件:

  1. 函数嵌套:必须存在一个外部函数和至少一个内部函数。
  2. 内部函数引用外部函数的变量:内部函数使用了外部函数作用域内的变量。
作用

闭包在 JavaScript 中有多种重要作用:

  • 读取函数内部的变量:外部函数执行结束后,其内部变量会被闭包保存,可通过闭包在外部访问这些变量。
  • 让这些变量的值始终保持在内存中:变量不会因外部函数执行完毕而被销毁,而是持续存在于内存里,方便后续使用。
  • 封装私有变量和方法:可以使用闭包来创建私有变量和方法,避免全局作用域的污染。
示例
function outerFunction() {// 外部函数的变量let counter = 0;// 内部函数,形成闭包function innerFunction() {counter++;return counter;}return innerFunction;
}// 创建闭包实例
const closure = outerFunction();// 调用闭包
console.log(closure()); // 输出: 1
console.log(closure()); // 输出: 2
console.log(closure()); // 输出: 3

在这个示例中,outerFunction 是外部函数,innerFunction 是内部函数。innerFunction 引用了 outerFunction 作用域内的 counter 变量,从而形成了闭包。当 outerFunction 执行完毕后,counter 变量不会被销毁,而是被 innerFunction 捕获并保留。每次调用 closure 函数时,counter 变量的值都会增加。

闭包的潜在问题

虽然闭包功能强大,但也可能带来一些问题,比如内存泄漏。由于闭包会让变量一直存在于内存中,如果闭包使用不当,可能会导致内存占用过高。因此,在使用闭包时,需要注意内存的使用情况,避免不必要的内存消耗。

8. 原型链

原型链是JavaScript中实现继承和对象属性查找的一种机制。以下是关于原型链的详细介绍:

原型的概念

在JavaScript中,每个对象都有一个原型(prototype)。原型也是一个对象,它可以包含一些属性和方法。当访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript引擎就会去它的原型对象中查找。

原型链的形成
  • 所有的对象都默认从 Object.prototype 继承属性和方法。例如,toString()valueOf() 等方法就是从 Object.prototype 继承来的。
  • 当创建一个函数时,JavaScript会自动为这个函数添加一个 prototype 属性,这个属性指向一个对象,称为该函数的原型对象。当使用构造函数创建一个新对象时,新对象的 __proto__ 属性(也称为原型链指针)会指向构造函数的原型对象。这样就形成了一条链,从新对象开始,通过 __proto__ 不断指向它的原型对象,直到 Object.prototype,这条链就是原型链。
原型链的作用
  • 实现继承:通过原型链,一个对象可以继承另一个对象的属性和方法。例如,定义一个 Animal 构造函数,再定义一个 Dog 构造函数,让 Dog 的原型指向 Animal 的实例,这样 Dog 的实例就可以继承 Animal 的属性和方法。
  • 属性和方法的共享:多个对象可以共享原型对象上的属性和方法,节省内存空间。比如,所有数组对象都共享 Array.prototype 上的 push()pop() 等方法。
示例代码
// 定义一个构造函数
function Person(name) {this.name = name;
}// 在构造函数的原型上添加方法
Person.prototype.sayHello = function() {console.log(`Hello, my name is ${this.name}`);
};// 创建一个Person的实例
const person1 = new Person('John');// 访问实例的属性和方法,先在实例本身查找,找不到就去原型上查找
person1.sayHello(); // 输出 "Hello, my name is John"
console.log(person1.__proto__ === Person.prototype); // 输出 true

在这个例子中,person1Person 构造函数的实例,它的 __proto__ 属性指向 Person.prototype。当调用 person1.sayHello() 时,由于 person1 本身没有 sayHello 方法,JavaScript会沿着原型链在 Person.prototype 上找到该方法并执行。

9. this指向

在 JavaScript 里,this 是一个特殊的关键字,它的指向取决于函数的调用方式。下面将为你详细介绍 this 在不同情况下的指向。

全局作用域中 this 的指向

在全局作用域里,this 指向全局对象。在浏览器环境中,全局对象是 window;在 Node.js 环境里,全局对象是 global

console.log(this === window); // 在浏览器环境中输出 true
this.globalVariable = 'I am a global variable';
console.log(window.globalVariable); // 输出: I am a global variable
函数作为普通函数调用时 this 的指向

当函数作为普通函数调用时,this 指向全局对象(在严格模式下,thisundefined)。

function normalFunction() {console.log(this);
}normalFunction(); // 在非严格模式下输出 window,在严格模式下输出 undefined
函数作为对象方法调用时 this 的指向

当函数作为对象的方法调用时,this 指向调用该方法的对象。

const person = {name: 'John',sayHello: function() {console.log(`Hello, my name is ${this.name}`);}
};person.sayHello(); // 输出: Hello, my name is John
构造函数中 this 的指向

当使用 new 关键字调用函数时,该函数就成为了构造函数,此时 this 指向新创建的对象。

function Person(name) {this.name = name;this.sayHello = function() {console.log(`Hello, my name is ${this.name}`);};
}const john = new Person('John');
john.sayHello(); // 输出: Hello, my name is John
callapplybind 方法对 this 指向的影响
  • call 方法call 方法可以调用一个函数,并且可以指定该函数内部 this 的指向。
function greet(message) {console.log(`${message}, my name is ${this.name}`);
}const person1 = { name: 'Alice' };
greet.call(person1, 'Hi'); // 输出: Hi, my name is Alice
  • apply 方法apply 方法和 call 方法类似,不同之处在于 apply 方法接受一个数组作为参数。
function greet(message) {console.log(`${message}, my name is ${this.name}`);
}const person2 = { name: 'Bob' };
greet.apply(person2, ['Hello']); // 输出: Hello, my name is Bob
  • bind 方法bind 方法会创建一个新的函数,在调用时会将 this 绑定到指定的对象上。
function greet(message) {console.log(`${message}, my name is ${this.name}`);
}const person3 = { name: 'Charlie' };
const boundGreet = greet.bind(person3);
boundGreet('Hey'); // 输出: Hey, my name is Charlie
箭头函数中 this 的指向

箭头函数没有自己的 this,它的 this 继承自外层函数。

const obj = {name: 'David',sayHello: function() {const arrowFunction = () => {console.log(`Hello, my name is ${this.name}`);};arrowFunction();}
};obj.sayHello(); // 输出: Hello, my name is David

理解 this 关键字的指向是 JavaScript 中的一个重要部分,不同的调用方式会导致 this 指向不同的对象。在实际开发中,要根据具体情况来确定 this 的指向。

相关文章:

  • Transformer 与 LSTM 在时序回归中的实践与优化
  • Mathematica中的ResourceFunction
  • 状态模式 VS 策略模式
  • Kotlin密封类优化Android状态管理
  • 从设计到应用:大尺寸PCB打样的关键领域解析
  • 使用 Spring Boot 构建 REST API
  • 【大语言模型ChatGPT+Python】近红外光谱数据分析及机器学习与深度学习建模(近红外光谱数据分析、定性/定量分析模型代码自动生成等)
  • setup 函数在 Vue 3 中的作用是什么?什么时候会执行
  • 打成jar 包以后,运行时找不到文件路径?
  • WebRTC 服务器之SRS服务器性能优化配置
  • 在 GitLab 中部署Python定时任务
  • 私有仓库 Harbor、GitLab
  • 云计算训练营笔记day02(Linux、计算机网络、进制)
  • UE5 材质淡入淡出
  • 【工具使用-数据可视化工具】Apache Superset
  • 超表面加工流程
  • 前端知识-forwardRef
  • 区块链+数据库:技术融合下的应用革新与挑战突围
  • Kubernetes笔记(1)Kubernetes入门
  • 解锁RAG:AI 2.0时代的“知识外挂”是如何炼成的?
  • “子宫内膜异位症”相关论文男性患者样本超六成?福建省人民医院展开调查
  • 文旅部:加强对景区索道、游船等设施设备安全隐患排查整治
  • 云南省司法厅党委书记、厅长茶忠旺主动投案,正接受审查调查
  • 长三角铁路今日预计发送386万人次,沪宁、沪杭等方向尚有余票
  • 苏杯登顶看到老将新人冲劲,国羽用冠军为奥运新周期开好头
  • 五一期间全国高速日均流量6200万辆,同比增长8.1%