首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

JavaScript 反射機制詳解:for...in 遍歷物件屬性與方法

     編輯:ASP.NET 基礎

前言

JavaScript 的反射指的是程式在執行期檢視、操作自身結構的能力——例如列舉物件的所有屬性與方法、動態呼叫函式。JS 原生提供 for...in 迴圈作為最基礎的反射工具。本文詳細說明 for...in 行為、原型鏈影響、屬性枚舉屬性、現代替代 API (Object.keys、Reflect.ownKeys) 以及實務應用:動態樣式套用。

for...in 基本語法


for (var prop in obj) {
    // prop: 字串,屬性名稱 (包含繼承自原型鏈的可枚舉屬性)
    // obj[prop]: 對應的值
}

關鍵行為特性

特性 說明
遍歷順序 ES2015+ 規範:整數索引升序 → 字串鍵建立順序 → Symbol 鍵建立順序。舊引擎可能不同。
包含繼承屬性 是,會遍歷原型鏈上所有 enumerable: true 的屬性。
排除不可枚舉 內建方法 (如 toString、valueOf) 預設 enumerable: false,不會出現。
Symbol 屬性 不會 被 for...in 遍歷。
刪除/新增屬性 迴圈中刪除屬性不會遍歷到;新增屬性可能或不會遍歷到 (視引擎)。

範例:基本遍歷與型別判斷


var obj = {
    name: "UEditor",
    version: "1.4.3",
    render: function() { console.log("rendering..."); },
    config: { theme: "default" }
};

// 手動加入原型屬性 (示範繼承)
Object.prototype.lib = "baidu";

for (var p in obj) {
    var val = obj[p];
    if (typeof val === "function") {
        console.log(`方法: ${p}`);
        // val(); // 動態呼叫
    } else {
        console.log(`屬性: ${p} = ${JSON.stringify(val)}`);
    }
}

/* 輸出:
屬性: name = "UEditor"
屬性: version = "1.4.3"
方法: render
屬性: config = {"theme":"default"}
屬性: lib = "baidu"   ← 來自 Object.prototype!
*/

過濾自有屬性:hasOwnProperty


for (var p in obj) {
    if (obj.hasOwnProperty(p)) { // 僅處理物件自身屬性
        console.log(p, obj[p]);
    }
}
// 或現代寫法
Object.keys(obj).forEach(p => console.log(p, obj[p]));

現代替代 API 比較

API 回傳 包含繼承 包含 Symbol 包含不可枚舉
for...in 迴圈 是 否 否
Object.keys(obj) string[] 否 否 否
Object.getOwnPropertyNames(obj) string[] 否 否 是
Object.getOwnPropertySymbols(obj) Symbol[] 否 是 否
Reflect.ownKeys(obj) (string|Symbol)[] 否 是 是
Object.entries(obj) [key, value][] 否 否 否

實務應用:動態樣式套用 (Dynamic Style Application)

問題場景

函式接收樣式物件,需套用至 DOM 元素。若直接 element.style = styleObj,會覆蓋元素原有的所有內建樣式,導致未指定的樣式 (如 height) 遺失。

錯誤做法


function setStyleBad(element, styleObj) {
    element.style = styleObj; // 錯誤:style 唯讀,且會整個替換
}

正確做法:反射遍歷僅更新指定屬性


function setStyle(element, styleObj) {
    if (!element || !styleObj) return;
    for (var prop in styleObj) {
        if (styleObj.hasOwnProperty(prop)) {
            // 瀏覽器自動處理 camelCase ↔ kebab-case 轉換
            // 如 backgroundColor → background-color
            element.style[prop] = styleObj[prop];
        }
    }
}

// 使用範例
var myStyle = {
    color: "#fff",
    backgroundColor: "#f00", // JS 用駝峰
    borderWidth: "2px",
    fontSize: "14px"
};

var el = document.getElementById("myDiv");
setStyle(el, myStyle);
// 僅更新上述 4 個屬性,原有 height、margin 等保持不變

進階:支援 kebab-case 鍵名


function setStyleFlexible(element, styleObj) {
    for (var key in styleObj) {
        if (!styleObj.hasOwnProperty(key)) continue;
        var value = styleObj[key];
        // 將 kebab-case 轉駝峰
        var jsKey = key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
        element.style[jsKey] = value;
    }
}

// 現在可直接用 CSS 鍵名
setStyleFlexible(el, {
    "color": "#fff",
    "background-color": "#f00",
    "border-width": "2px"
});

反射在繼承中的應用:混入


function mixin(target, source) {
    for (var key in source) {
        if (source.hasOwnProperty(key)) {
            target[key] = source[key];
        }
    }
    return target;
}

var base = { init() { console.log("base init"); } };
var feature = { 
    doSomething() { console.log("feature"); },
    prop: 123
};

var combined = mixin(Object.create(base), feature);
combined.init(); // base init
combined.doSomething(); // feature

效能與最佳實務

  1. 避免在熱路徑使用 for...in:遍歷原型鏈較慢,優先用 Object.keys / Object.entries。
  2. 永遠搭配 hasOwnProperty:除非刻意要包含繼承屬性。
  3. 陣列請勿用 for...in:會遍歷索引字串、原型方法、順序不保證。請用 for...of、forEach、for (let i=0; i<len; i++)。
  4. 動態屬性存取:obj[prop] 是反射的核心,配合 typeof 判斷函式/值。
  5. 嚴格模式下:with 陳述式被禁用,反射更依賴 for...in / Object.* API。

總結

  • JavaScript 反射的基石是 for...in,能遍歷物件可枚舉屬性(含繼承)。
  • 務必用 hasOwnProperty 過濾自有屬性,或改用 Object.keys 等現代 API。
  • 經典應用:動態樣式合併——遍歷樣式物件鍵值,逐一賦予 element.style[prop],避免整體覆蓋。
  • 理解 enumerable、Symbol、原型鏈對遍歷的影響,是撰寫健壯動態程式碼的關鍵。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved