JavaScript面向对象程序设计:原型、属性共享与屏蔽机制详解

作者:函数茶馆时间:2026-10-23 09:59:01浏览:349

本文深入解析JavaScript面向对象编程中的核心概念,重点探讨原型中引用类型属性的共享机制及其对实例的影响。通过对比值类型属性、属性屏蔽理论及hasOwnProperty方法,阐明构造函数与原型链的优先级关系。同时解析constructor与__proto__的默认行为及修改后的指向变化,帮助开发者准确理解对象属性查找与继承逻辑。

本文总结JavaScript面向对象程序设计实践中常用的关键知识点,旨在为开发者提供清晰的参考与指导。

原型中引用类型属性的共享特性

实践一:原型中引用类型属性的共享特性


var Person = function(){};
Person.prototype = {
  info:{
    "name":"Tom"
  }
}
var p1 = new Person();
var p2 = new Person();
p1.info.name = '我是p1';
p2.info.name = '我是p2';
console.log(p1.info.name); // 我是p2
console.log(p2.info.name); // 我是p2

分析:p1和p2均为通过构造函数实例化的对象。由于info是引用类型属性且定义在原型中,p1.info与p2.info指向堆内存中的同一块区域。当执行p1.info.name赋值后,再执行p2.info.name赋值,两者修改的是同一内存地址的数据。因代码自上而下执行,最终console输出时,p1.info.name与p2.info.name均显示为'我是p2'。结论:原型中定义的引用类型属性在所有实例间是共享的。

实例化后引用类型属性地址变更

实践二:实例化后重新赋值引用类型属性导致地址变更


var Person = function(){};
Person.prototype = {
  info:{
    "name":"Tom"
  }
}
var p1 = new Person();
var p2 = new Person();
p1.info = {"name":"哈哈"};;
console.log(p1.info.name); // 哈哈
console.log(p2.info.name); // Tom

分析:当代码执行至p1.info = {"name":"哈哈"}时,p1的info属性不再指向原型中的共享对象,而是指向了新创建的内存地址。此时p2.info未做任何修改,仍指向原型中原有的对象地址。因此,p1.info.name输出'哈哈',而p2.info.name仍保持原型中的原始值'Tom'。

值类型属性与属性屏蔽机制

实践三:对象中值类型属性的非共享特性


var Person = function(name,age){
  this.name = name;
  this.age = age;
};
var p1 = new Person('Tom',10);
var p2 = new Person('Lucy','8');
console.log(p1.name); // Tom
console.log(p2.name); // Lucy

实践四:属性屏蔽理论


var Person = function () {
  this.name = '小明',
  this.buy = function () {
    console.log('去买面包');
  }
};
Person.prototype = {
  name:"原型中的小明",
  buy:function() {
    console.log('去买汽水');
  },
  age:10
}
var p = new Person();
// 正常情况下我们去访问p里的属性和方法。
// 可以看出原型中的属性和方法的优先级没有构造函数中的高,如果构造函数中没有,会去原型中查找
console.log(p.name);// 小明
p.buy();// 控制台输出 去买面包
console.log(p.age); // 10
// 如果我们要访问原型中的属性和方法可以这样
delete p.name; // 此处会删除构造函数中的属性
console.log(p.name);// 原型中的小明
Person.prototype.buy(); // 控制台输出 去买汽水

hasOwnProperty方法与原型链属性判断

实践五:对象中的hasOwnProperty方法


var Person = function () {
  this.name = 'Tom';
}
Person.prototype = {
  age:30
}
var p = new Person();
console.log(Person.hasOwnProperty('name')); // true
console.log(p.hasOwnProperty('name')); // true
console.log(Person.hasOwnProperty('age')); // false
console.log(p.hasOwnProperty('age')); // false
var ClassRoom = {
  contain:100,
  leader:'小明',
  teacher:'王老师'
}
console.log(ClassRoom.hasOwnProperty('leader')); // true
console.log( p.hasOwnProperty === Object.prototype.hasOwnProperty ); //true
console.log( Person.hasOwnProperty === Object.prototype.hasOwnProperty ); //true
console.log( ClassRoom.hasOwnProperty === Object.prototype.hasOwnProperty ); //true

分析:hasOwnProperty方法用于判断属性是否直接存在于对象自身,而非从原型链继承。该方法可通过构造函数和实例对象访问,且仅检查构造函数定义或字面量直接赋值的属性。hasOwnProperty方法本身继承自Object原型。

constructor与__proto__属性解析

实践六:自定义对象中的constructor与__proto__属性(涉及原型链问题,后续章节详述)


/* 每个实例对象都会默认生成一个constructor 和 __proto__ 属性 这一条不管是内置对象还是自定义对象都是一样的 */
function A() {};
var a = new A();
console.log(a.constructor === A); // true 默认情况下实例的constructor指向实例的构造函数
console.log(a.__proto__ === A.prototype); // true 默认情况下,实例的__proto__属性指向类的prototype
/* 当我们修改 constructor 和 __proto__ 属性时候 */
function B() {};
// 手动修改原型链
B.prototype = {
 __proto__:String.prototype
}
var b = new B();
// instanceof 用来判断obj1是否是obj2的一个实例,可见用 instanceof 来判断数据类型也是不准确的。
// 这里涉及到继承,而js面向对象中的继承就是通过原型链来实现的。
console.log(b instanceof B); // true
console.log(b instanceof String); // true
console.log(b instanceof Object); // true
console.log(b.constructor === B); // false 此处b的构造函数不是B了,因为原型链指到String上去了。
console.log(b.constructor === String); //true
// 手动修改b的constructor ,如果自己不设置,那么就会去寻找原型链,如上
//b.constructor = B; 或者 B.prototype.constructor = B;
// 手动设置后,就会按照设置的来走,如下:
b.constructor = B;
console.log(b.constructor === B); // true
console.log(b.__proto__=== B.prototype); // true
console.log(B.prototype.__proto__=== String.prototype); // true;
// 下面这两个也是原型链的内容
// B是函数,函数的constructor 是 Function,函数的__proto__ 是Function.prototype
console.log(B.constructor === Function); // true
console.log(B.__proto__ === Function.prototype); // true

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

作者最新文章