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 的指向。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/904400.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/904400.shtml
英文地址,請注明出處:http://en.pswp.cn/news/904400.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

知識圖譜 + 大語言模型:打造更聰明、更可靠的AI大腦 —— 探索 GraphRAG 中文優化與可視化實踐

大語言模型&#xff08;LLMs&#xff09;無疑是近年來人工智能領域最耀眼的明星。它們強大的自然語言理解和生成能力&#xff0c;在文本創作、代碼生成、對話交互等眾多領域展現了驚人的潛力。然而&#xff0c;當前的 LLMs 并非完美無缺&#xff0c;它們常常面臨著“幻覺”&…

【uniapp】在UniApp中檢測手機是否安裝了某個應用

1. 使用plus.runtime.isApplicationExist&#xff08;僅限App端&#xff09; // 判斷應用是否安裝 function checkAppInstalled(packageName) {if (uni.getSystemInfoSync().platform android || uni.getSystemInfoSync().platform ios) {// 僅App端可用if (typeof plus ! u…

使用 Vue + Axios 構建與后端交互的高效接口調用方案

使用 Vue Axios 構建與后端交互的高效接口調用方案 在 Vue 前端開發中&#xff0c;與后端接口的數據交互是非常核心的部分。而 Axios 是 Vue 項目中最常用的 HTTP 客戶端&#xff0c;具備基于 Promise、攔截器、自定義實例等諸多優勢。 本篇將深入介紹如何基于 Vue 搭配 Axi…

RN學習筆記 ?

太無聊了最近&#xff0c;找點事做&#xff0c;學一下RN豐富一下技術棧&#x1fae1;。但是開發APP除了RN&#xff0c;還有一種選擇就是WebView&#xff0c;但是基于WebView的APP的性能被普遍認為不如RN&#xff0c;因為WebView本質上是一個容器&#xff0c;用于在應用中嵌入網…

聊天助手提示詞調優案例

一、背景 今天有粉絲說自己的聊天助手提示詞輸出的效果不好&#xff0c;輸出的內容不是太呆板就是太浮夸&#xff0c;希望更像真人一樣。 本文介紹幾個調優方法&#xff0c;希望對大家有啟發。 二、調優 《系統掌握大語言模型提示詞 - 從理論到實踐》提示詞小冊中介紹了很多…

5.6 react組件化開發基礎

react 組件開發基礎 組件分類與組件使用 組件傳參 父傳子 【函數數據傳值 實參 形參對應關系】 子傳父 插槽 透傳 useContext 上下文&#xff08;作用域&#xff09; 跨層級調用方法 通過子組件的實例對象useRef 直接調用子組件的方法 和數據 狀態管理&#xff08;非常多…

【SF順豐】順豐開放平臺API對接(Java對接篇)

對接前置篇&#xff1a; 【SF順豐】順豐開放平臺API對接&#xff08;注冊、API測試篇&#xff09;_順豐api接口對接指南-CSDN博客 1.實現效果展示 2.SF順豐開放平臺&#xff0c;JDK資源下載。 下載地址&#xff1a;順豐開放平臺 3.將下載的JDK放入項目中。 4.將JDK資源引入p…

我用cursor 搭建了臨時郵箱服務-Temp Mail 365

用業余時間搭建了一個臨時郵箱&#xff0c;對于后端程序員出身的我&#xff0c;對前端了解的不太多&#xff0c;有了cursor的幫助&#xff0c;補齊了自己的短板&#xff0c;搭建了這個服務&#xff0c;下面對臨時郵箱架構設計與安全性做一個分析。 https://temp-mail-365.com 臨…

破解工業3D可視化困局,HOOPS Visualize助力高效跨平臺協作與交互!

一、當前3D可視化面臨的痛點 &#xff08;1&#xff09;性能瓶頸 現有的許多3D可視化工具在處理大型復雜模型時往往力不從心。例如在航空航天、汽車制造等高端制造業&#xff0c;動輒涉及數以億計的三角面片和海量的紋理細節。這些超大規模的模型在渲染時常常出現卡頓、延遲&…

1、Kafka與消息隊列核心原理詳解

消息隊列&#xff08;Message Queue, MQ&#xff09;作為現代分布式系統的基礎組件&#xff0c;極大提升了系統的解耦、異步處理和削峰能力。本文以Kafka為例&#xff0c;系統梳理消息隊列的核心原理、架構細節及實際應用。 Kafka 基礎架構及術語關系圖 術語簡要說明 Produce…

2025年北京市職工職業技能大賽第六屆信息通信行業網絡安全技能大賽初賽-wp

- -考試當場沒做出來 后面做的 misc ? cd misc ? ls num.docx num.zip ? unzip num.docx Archive: num.docxinflating: [Content_Types].xmlinflating: _rels/.relsinflating: word/document.xmlinflating: word/_rels/document.xml.relsextracting: word/media/image1.jp…

JavaScript 到命令和控制 (C2) 服務器惡意軟件分析及防御

攻擊始于一個經過混淆的JavaScript文件,該文件從開源服務中獲取編碼字符串以執行PowerShell腳本。然后,該腳本從一個IP地址和一個URL縮短器下載一個JPG圖像和一個文本文件,這兩個文件都包含使用隱寫術嵌入的惡意MZ DOS可執行文件。這些有效載荷一旦執行,就會部署Stealer惡意…

【計網】ipconfig、ping、arp、tracert

目錄 ipconfig ping arp tracert cmd ipconfig ipcofig -all IPv4 物理地址 ping 檢測網絡連通情況&#xff0c;分析網絡速度 根據域名得到服務器IP 根據TTL判斷對方所使用的操作系統以及數據包經過路由器數量 byte數據包大小 time響應時間 TTLDNS記錄在DNS服務器上存在…

WiFi那些事兒(八)——802.11n

目錄 802.11n 技術簡介與測試項 一、802.11n 技術簡介 &#xff08;一&#xff09;標準概述 &#xff08;二&#xff09;關鍵技術特性 1. MIMO&#xff08;多輸入多輸出&#xff09;技術 2. 信道綁定&#xff08;Channel Bonding&#xff09; 3. 幀聚合&#xff08;Fram…

碼蹄集——直角坐標到極坐標的轉換、射線、線段

目錄 MT1052 直角坐標到極坐標的轉換 MT1066 射線 MT1067 線段 MT1052 直角坐標到極坐標的轉換 思路&#xff1a; arctan()在c中是atan()&#xff0c;結果是弧度要轉換為度&#xff0c;即乘與180/PI 拓展&#xff1a;cos()、sin()在c代碼中表示方式不變 #include<bits/…

深入解析 Linux/Unix 通信機制:從原理到觀測實踐

深入解析 Linux/Unix 通信機制&#xff1a;從原理到觀測實踐 配圖建議&#xff1a;Linux系統架構與通信機制全景示意圖 一、開篇&#xff1a;理解“一切皆文件”的哲學 Unix/Linux 操作系統的核心靈魂在于其獨特的設計哲學。當 Dennis Ritchie 和 Ken Thompson 在貝爾實驗室開…

spring上傳文件添加水印

1、實現 MultipartFile package com.pojo.common.core.domain;import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream;import org.springframework.lang.Nullable; import org.springframework.util.Assert; im…

嵌入式MCU語音識別算法及實現方案

在嵌入式MCU&#xff08;微控制器單元&#xff09;中實現語音識別&#xff0c;由于資源限制&#xff08;如處理能力、內存、功耗等&#xff09;&#xff0c;通常需要輕量級算法和優化技術。以下是常見的語音識別算法及實現方案&#xff1a; 一、傳統語音識別算法 動態時間規整&…

【論文閱讀】DETR+Deformable DETR

可變形注意力是目前transformer結構中經常使用的一種注意力機制&#xff0c;最近補了一下這類注意力的論文&#xff0c;提出可變形注意力的論文叫Deformable DETR&#xff0c;是在DETR的基礎上進行的改進&#xff0c;所以順帶著把原本的DETR也看了一下。 一、DETR DETR本身是…

大模型在宮頸癌診療全流程預測與應用研究報告

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與創新點 二、大模型預測宮頸癌術前風險 2.1 術前數據收集與預處理 2.2 預測模型構建與算法選擇 2.3 術前風險預測指標與案例分析 三、大模型輔助制定術中方案 3.1 術中風險動態監測與預測 3.2 基于預測的手術方案優化…