构造函数是一个用于创建和初始化一个对象的特殊函数。在 JavaScript 中,几乎每个对象都是由某个构造函数创建的。构造函数的约定是,其名称首字母大写。
如何定义它?
你可以像定义常规函数一样定义构造函数,但是为了创建新的实例,你需要使用 new
关键字。
以下是一个简单的例子:
// 定义一个构造函数
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.getFullName = function() {
return this.firstName + " " + this.lastName;
};
}
// 使用构造函数创建新的对象
var john = new Person("John", "Doe");
var jane = new Person("Jane", "Smith");
console.log(john.getFullName()); // 输出:John Doe
console.log(jane.getFullName()); // 输出:Jane Smith
在这个例子中:
Person
是一个构造函数,它接受两个参数,firstName
和 lastName
。this
关键字,我们可以给创建的对象分配属性和方法。new
关键字,我们创建了 Person
的新实例。注意:更好的做法是使用原型链来给所有对象共享方法,如:Person.prototype.getFullName = function() {...}
。只在原型链上有一个方法,但是所有对象可用。在构造函数里面创建getFullName会导致所有的对象都有,占用大量内存
在 JavaScript 中,原型 (prototype
) 是一个对象的内部链接,通过这个链接,对象可以继承其他对象的属性和方法。原型是 JavaScript 的核心特性,支撑了它的原型继承机制。
每个 JavaScript 对象都有一个原型吗?
是的,几乎每个 JavaScript 对象都有一个原型。唯一没有原型的对象是通过 Object.create(null)
创建的对象。
当你试图访问一个对象的属性或方法,而该对象本身没有这个属性或方法,JavaScript 会查找该对象的原型(以及原型的原型,以此类推)直到找到该属性或方法或达到原型链的尽头(即 null
)。
原型(prototype)是一个对象,它包含共享属性和方法的对象。
原型链(prototype chain)是由一系列原型对象组成的链式结构。
通过原型链,JavaScript可以实现对象之间的继承和共享属性和方法。
__proto__
和 prototype
是 JavaScript 中经常被提及,但同时也是经常被混淆的两个属性。它们之间的关系和用途非常核心,对于理解 JavaScript 的原型继承机制至关重要。
prototype
属性:
prototype
是 JavaScript 中的函数对象特有的属性__proto__
属性:__proto__
是**每个 JavaScript 对象(**除了 null
)都具有的一个属性,它是一个访问器属性(一个 getter 函数和一个 setter 函数)。关系与区别:
对于自定义构造函数,其创建的实例的 __proto__
属性指向构造函数的 prototype
属性。例如:
function MyConstructor() {}
let obj = new MyConstructor();
console.log(obj.__proto__ === MyConstructor.prototype); // true
只有函数对象才有 prototype
属性,而所有的对象都有 __proto__
属性。
Object.getPrototypeOf(obj)
方法:这个方法返回对象 obj
的原型。例如:var person = {
name: "John",
age: 30,
};
var student = Object.create(person);
console.log(Object.getPrototypeOf(student)); // 输出 person 对象
obj.__proto__
属性:__proto__
是一个非标准的属性,但在大多数现代浏览器中支持。它返回对象 obj
的原型。例如:var person = {
name: "John",
age: 30,
};
var student = Object.create(person);
console.log(student.__proto__); // 输出 person 对象
需要注意的是,虽然 __proto__
属性在大多数情况下是有效的,但它不是 JavaScript 标准的一部分,因此不建议在生产环境中广泛使用。
instanceof
操作符:instanceof
操作符可用于检查一个对象是否是某个构造函数的实例,其实现原理是通过原型链进行判断。例如:function Person(name) {
this.name = name;
}
var john = new Person("John");
console.log(john instanceof Person); // 输出 true
console.log(john instanceof Object); // 输出 true(所有对象都是 Object 的实例)
instanceof
操作符可以判断对象是否是某个构造函数的实例,间接地可以判断对象的原型是否是某个对象。
这些方法都可以用来检查一个对象的原型,具体选择哪种方法取决于个人偏好和项目需求。通常情况下,推荐使用 Object.getPrototypeOf(obj)
方法来获取对象的原型,因为它是标准的方法且具有广泛的兼容性。
潜在的覆盖问题:如果原型上已经存在了一个同名的方法或属性,你的修改可能会意外地覆盖它,或者在未来的版本中被新的原型方法覆盖。
解决方法:
如果你确实需要修改或扩展原型(虽然这通常不推荐),以下是一些建议:
不修改全局对象的原型:特别是不要修改原生对象,如Array
、Object
、String
等的原型。如果你想为原生对象添加功能,可以考虑使用工具函数或类/对象扩展。
使用Object.defineProperty:使用Object.defineProperty
可以让你更加细致地定义原型上的属性或方法,例如设置为不可枚举,这样它就不会在for...in
循环中出现。
Object.defineProperty(Array.prototype, 'myCustomFunction', {
value: function() {
// ...
},
writable: true,
configurable: true,
enumerable: false
});
检查方法是否已存在:在添加新的方法或属性前,检查它是否已存在,以避免意外的覆盖。
if (!Array.prototype.myCustomFunction) {
Array.prototype.myCustomFunction = function() {
// ...
};
}
回答属性查找:当试图访问一个对象的属性时,JavaScript 首先在该对象本身上查找该属性。如果没有找到,它会在对象的原型上查找,然后是原型的原型,依此类推,直到找到属性或达到原型链的末尾。
原型链继承:4种方式,class的extend继承,在原型上new一个父类,function + call,原型链+call
hasOwnProperty
方法用于检查一个对象是否具有指定名称的自身属性(即非继承而来的属性)。它接受一个参数,即属性的名称,如果对象拥有该属性,则返回 true
,否则返回 false
。这个方法对于遍历对象自身的属性非常有用,可以用来确定属性的来源是否是对象本身而不是继承的。
总结来说,hasOwnProperty
方法是所有普通对象通过原型链继承自 Object.prototype
的方法,用于判断一个对象是否具有指定名称的自身属性。
原型污染(Prototype Pollution)是指通过修改对象的原型链,对对象的原型进行恶意或意外的更改,从而影响到对象及其相关属性和方法的行为。这种污染可以导致安全漏洞和意外的行为。
原型污染之所以可能是危险的,有以下几个原因:
污染全局对象:通过污染全局对象的原型,可以影响到所有基于该原型创建的对象。这可能导致全局范围内的意外行为和冲突,影响到整个应用程序。
覆盖或篡改原始对象的属性和方法:通过修改对象原型链上的方法或属性,可以改变对象的行为。这可能导致不可预料的结果和潜在的安全风险。例如,攻击者可以修改内置对象的原型,改变其方法的行为,从而实现代码执行、信息泄露或其他攻击。
绕过安全检查和访问控制:某些安全机制和访问控制是基于对象的原型链的。通过修改原型链,攻击者可以绕过这些机制,获取未授权的访问权限,从而导致数据泄露、越权访问和其他安全问题。
原型污染可以通过多种方式发生,其中常见的一种是通过用户输入直接或间接地修改对象的原型链。例如,当应用程序使用不可信的用户输入直接作为属性名或方法名时,攻击者可以通过构造特定的输入来修改原型链。
为了防止原型污染,可以采取以下措施:
总之,原型污染是一种潜在的安全问题,攻击者可以通过修改对象的原型链来实现不当行为和攻击。为了保护应用程序的安全性,开发人员应了解原型污染的概念,并采取适当的措施来防止和缓解该问题。