一、构造函数(完整代码示例)
构造函数就是一个普通函数,创建方式和普通函数没有区别,不同的是首字母习惯上大写。构造函数和普通函数的区别就是调用方式不同 普通函数直接调用fun(),构造函数需要使用new关键字来调用(var per=new Person();)
1. 构造函数与普通函数的区别(调用方式)
// 1. 普通函数(首字母小写,直接调用) function sayHello() { console.log("普通函数调用"); this.name = "普通函数"; // 非严格模式下this指向window } // 普通调用 sayHello(); // 输出:普通函数调用 console.log(window.name); // 输出:普通函数(验证this指向) // 2. 构造函数(首字母大写,new调用) function Person(name, age) { // 执行流程: // 1. 隐式创建新对象;2. this指向新对象;3. 执行代码;4. 返回新对象 this.name = name; this.age = age; this.sayHi = function() { console.log(`我是${this.name},今年${this.age}岁`); }; } // 构造函数调用(创建实例) const per1 = new Person("张三", 20); const per2 = new Person("李四", 22); console.log(per1); // 输出:Person { name: '张三', age: 20, sayHi: [Function] } per1.sayHi(); // 输出:我是张三,今年20岁 per2.sayHi(); // 输出:我是李四,今年22岁 // 3. instanceof检查实例归属 console.log(per1 instanceof Person); // 输出:true(per1是Person的实例) console.log(per1 instanceof Object); // 输出:true(所有对象都是Object的后代) console.log(per1 instanceof Array); // 输出:false(验证非Array实例)构造函数的执行流程:
1、立刻创建一个新的对象
2、将新建的对象设置为函数中的this,在构造函数中可以使用this来引用新建的对象
3、逐行执行构造函数中的代码
4、将新创建的对象作为返回值返回
使用同一个构造函数创建的对象属于同一类对象,我们也将一个构造函数称为一个类。通过一个构造函数创建的对象,称为该类的实例,也称为该构造函数的实例。
使用instanceof可以检查一个对象是否是一个类的实例
所有对象都是Object的后代
二、原型(prototype)(完整代码示例)
我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,叫原型对象,每个函数都有,都指向一个不同的原型对象。
如果函数作为普通函数调用,prototype没有任何作用;如果函数以构造函数形式调用时,它所创建的对象中都会有一个隐含的属性__proto__,指向该构造函数的原型对象,我们可以通过__proto__访问该属性。
原型对象就相当于一个公共区域,所有同一个类的实例都可以访问到这个原型对象。我们可以将对象中共有的内容,统一设置到原型对象中。这样不用分别为每一个实例对象添加,也不会影响到全局作用域,就可以使每个对象中都有这些属性和方法。
1. 原型对象的基本使用(公共属性 / 方法)
// 构造函数 function Student(name, score) { this.name = name; // 实例自身属性 this.score = score; } // 原型对象添加公共方法(所有实例共享) Student.prototype.sayScore = function() { console.log(`${this.name}的成绩是${this.score}分`); }; // 原型对象添加公共属性 Student.prototype.school = "第一中学"; // 创建实例 const stu1 = new Student("小明", 90); const stu2 = new Student("小红", 85); // 访问实例自身属性 console.log(stu1.name); // 输出:小明(自身属性) // 访问原型对象的属性/方法 console.log(stu1.school); // 输出:第一中学(原型属性) stu2.sayScore(); // 输出:小红的成绩是85分(原型方法) // 验证:所有实例共享原型方法(内存复用) console.log(stu1.sayScore === stu2.sayScore); // 输出:true2. 属性查找规则(自身 → 原型 → 原型的原型)
当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,有直接使用,如果没有会去原型对象中找,如果找到则直接使用
// 接上面的Student构造函数 // 1. 自身有属性,优先使用自身 stu1.score = 95; console.log(stu1.score); // 输出:95(自身覆盖原型) // 2. 自身无,找原型;原型无,找原型的原型;直到Object原型(null) console.log(stu1.toString()); // 输出:[object Object](来自Object.prototype) console.log(stu1.abc); // 输出:undefined(最终未找到)3. in 与 hasOwnProperty 区别
使用in检查对象中是否含有某个属性时,如果对象中没有但是原型对象中有,也会返回true;
使用对象的hasOwnProperty("name"),检查对象自身中是否含有该属性,只有对象自身中有时才会返回true。
// 接上面的stu1实例 // 1. in:自身或原型有则返回true console.log("name" in stu1); // 输出:true(自身有) console.log("school" in stu1); // 输出:true(原型有) console.log("toString" in stu1); // 输出:true(Object原型有) // 2. hasOwnProperty:仅自身有则返回true console.log(stu1.hasOwnProperty("name")); // 输出:true(自身有) console.log(stu1.hasOwnProperty("school")); // 输出:false(原型有) console.log(stu1.hasOwnProperty("toString")); // 输出:false(Object原型有)4. 原型链示例(多层原型查找)
// 自定义构造函数 function Animal(type) { this.type = type; } // Animal原型添加方法 Animal.prototype.eat = function() { console.log(`${this.type}会吃东西`); }; // Dog继承Animal(简化版,仅演示原型链) function Dog(name) { this.name = name; } // 将Dog的原型指向Animal的实例(形成原型链) Dog.prototype = new Animal("狗"); // 创建Dog实例 const dog1 = new Dog("旺财"); // 原型链查找: // dog1自身 → Dog.prototype(Animal实例) → Animal.prototype → Object.prototype → null console.log(dog1.name); // 自身:旺财 dog1.eat(); // Animal.prototype:狗会吃东西 console.log(dog1.toString()); // Object.prototype:[object Object] console.log(dog1.xxx); // 输出:undefined(最终未找到)三、完整整合示例(构造函数 + 原型 + 原型链)
原型对象也是对象,所以它也有原型。当我们使用一个对象的属性或方法时,会先在自身中寻找。自身中如果有,直接使用;如果没有则去原型对象中找,如果原型对象中有,则直接使用;如果没有则去原型对象中的原型去找,直到找到Object对象的原型。Object对象的原型没有原型(对应的值为null),如果还没有找到则返回undefined。
// 1. 定义构造函数 function Car(brand, color) { // 实例自身属性 this.brand = brand; this.color = color; } // 2. 原型添加公共方法(所有Car实例共享) Car.prototype.run = function() { console.log(`${this.color}的${this.brand}正在行驶`); }; // 原型添加公共属性 Car.prototype.wheels = 4; // 3. 创建实例 const car1 = new Car("宝马", "白色"); const car2 = new Car("奔驰", "黑色"); // 4. 访问属性/方法 console.log(car1.brand); // 自身:白色 console.log(car1.wheels); // 原型:4 car2.run(); // 原型方法:黑色的奔驰正在行驶 // 5. 检查属性归属 console.log("wheels" in car1); // true(原型有) console.log(car1.hasOwnProperty("wheels")); // false(自身无) console.log(car1.hasOwnProperty("brand")); // true(自身有) // 6. 原型链验证 console.log(car1.__proto__ === Car.prototype); // true(实例的__proto__指向构造函数原型) console.log(Car.prototype.__proto__ === Object.prototype); // true(原型的原型指向Object原型) console.log(Object.prototype.__proto__); // null(Object原型无原型)四、核心总结
- 构造函数通过
new调用,自动创建 / 返回对象,this指向实例; - 原型(
prototype)是构造函数的属性,存储实例共享的属性 / 方法,减少内存占用; - 属性查找遵循「自身 → 原型 → 原型链」规则,直到
Object.prototype.__proto__(null); in检查整个原型链,hasOwnProperty仅检查实例自身。