面向对象是什么?
是一种编程思想,是基于面向过程实现的,不需要关注底层是如何实现的,直接调用即可
创建对象的方式
1. 字面量创建
let obj = {
name: "zhangsan",
age: 18,
like:()=>console.log("run")
}
2. 内置构造函数创建
let stu = new Object();
stu.name = "zhangsan";
stu.age = 18;
stu["like"] = ()=>console.log("run");
let variable = 'grender';
stu[variable] = '男';
console.log(stu1); // {name: "zhangsan", age: 18, grender: "男", like: ƒ}
3. 工厂函数创建
// 构建工厂函数
function createObj(){
var obj = new Object();
// 往对象添加成员
obj.name = "zhangsan";
obj.age = "18";
obj.like = ()=>console.log("run");
return obj;
}
// 使用工厂函数创建对象
var objStr1 = createObj();
var objStr2 = createObj();
4. 自定义构造函数创建对象
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
var ps1 = new Person("zhangsan",18,"男");
var ps2 = new Person("xiaofang",17,"女");
通过new构造函数创建对象,做了以下几个事情
1. 在内存开辟了一个空间
2. 将关键字 this 指向了这个空间
3. 执行构造函数中的代码(添加数据)
4. 将 this 指向的实例对象返回
构造函数的使用
// 自定义手机的构造函数
function Phone(name, banner, size, color, price) {
this.name = name;
this.banner = banner;
this.size = size;
this.color = color;
this.price = price;
this.call = () => console.log('打电话....');
}
// 实例化对象
let apple1 = new Phone('苹果12', 'iphone', '6.7', '玫瑰紫', 12999);
let apple2 = new Phone('苹果13', 'iphone', '6.7', '玫瑰紫', 15999);
console.log(apple1); // 结果就是一个对象包含成员属性和值
// 对象函数调用
apple1.call(); // 打电话....
apple2.call(); // 打电话....
// 成员属性值获取
console.log(apple1.price); // 12999
console.log(apple1["price"]); // 12999
// 对象属性的修改
apple1.price = 19999;
console.log(apple1.price); // 19999
console.log(apple1.call == apple2.call); // false
这里会发现同一个构造函数创建的方法,实现的效果一样,但是调用的是两个方法
如果创建了多个对象,则每个对象都需要给call方法开辟方法空间,造成内存冗余
原型
为了解决构造函数的缺点(多个对象使用了一样的函数,占用了多个内存空间浪费)
prototype
每一个函数自带的一个成员,是一个对象空间,即构造函数也是函数,也有这个对象空间
function Person() {}
console.log(Person.prototype)
// 是一个对象
// Object
// constructor: ƒ Person()
// __proto__: Object
// 往该对象放入属性和属性值
Person.prototype.name = "zhangsan";
Person.prototype.sayHi = function () {
console.log("打电话...");
}
在函数的prototype里面存储的内容,不是给函数使用的,是给函数每一个实例化对象使用的
__proto__
每一个对象都自带的一个成员,是一个对象空间,即实例化对象也是对象,也有这个对象空间
这个对象空间是给每一个对象使用的
constructor
这个属性在对象的__proto__里面,存在于原型对象中,是指当前这个对象所属的构造函数
function Person(name) {
this.name = name;
// this.call = function(){
// console.log("打电话");
// }
}
// 在构造函数的原型对象中添加方法
Person.prototype.call = function () {
console.log("打电话...");
}
let per1 = new Person("zhangsan");
// 向原型对象中添加方法或属性,不推荐通过对象的__proto__向原型中添加属性或方法
per1.__proto__.eat = function () {
console.log('好吃就多吃点..');
}
let per2 = new Person("lisi");
console.log(per1); // zhangsan
console.log(per2); // lisi
per1.eat() // 好吃就多吃点..
per1.call() // 打电话...
// 构造器
console.log(per1.__proto__.constructor === Person); // true
console.log(Person.prototype.constructor === Person); //true
// 将方法写到构造函数中,会造成内存的浪费
console.log(per1.call== per2.call); // true
console.log(per1.__proto__ === Person.prototype); // true
原型总结
1. 每一个函数都有prototype属性,指向构造函数的原型对象,一个标准属性
2. 每一个对象都有__proto__属性,指向创建这个对象的构造函数的原型对象,一个非标准属性
3. prototype和__proto__所属的构造函数是同一个对象空间
4. 在原型对象中都一个constructor属性,这是原型对象的构造器,constructor属性指向这个原型的构造函数
5. 创建构造函数的时候,属性写在函数体内,方法写在原型上
原型链
// 自定义构造函数
function Person(name) {
this.name = name;
}
// 向原型对象中添加方法
Person.prototype.call = function () {
console.log("打电话...");
}
// 实例化对象
let p1 = new Person("zhangsan");
console.log(p1); // name: "zhangsan" __proto__
console.log(Person); // 函数体
console.log(Person.prototype); // call() constructor __proto__
console.log(p1.__proto__.__proto__); // constructor.....
console.log(p1.__proto__.__proto__ === Object.prototype); // true
console.log(p1.__proto__.__proto__.__proto__); // null
console.log(Object.prototype); // constructor.....
console.log(Object.prototype.__proto__); // null
内置构造函数Object的prototype指向的原型对象的__proto__属性指向是一个null
原型的终点指向null
构造函数的prototype指向的原型对象和实例化对象的__proto__指向的原型是同一个
对象都有一个__proto__属性,指向原型对象
原型对象也是对象,原型对象中也有__proto__属性,指向的也是一个原型对象
原型对象的原型一直往下找,最终都会找到顶级对象Object构造函数的prototype的原型
而Object的prototype指向的原型对象的__proto__指向的是一个null
__proto__串联起来的对象链状结构叫做原型链
对象访问属性或方法的访问规则
1. 先在对象自身查找,如果有该属性或者方法则直接使用
2. 如果没有,就去__proto__原型中查找,找到直接使用
3. 如果没有,就去__proto__指向的原型中查找,找到直接使用
4. 如果没有,则继续向原先对象的原型中查找,直到找到Object的prototype指向的原型
还是没有则返回undefined
特殊的原型链
let div = document.querySelector('div');
// 查看元素对象
console.dir(div);
console.log(div.__proto__ === HTMLDivElement.prototype); // true
console.log(div.__proto__.__proto__ === HTMLElement.prototype); // true
console.log(div.__proto__.__proto__.__proto__ === Element.prototype); // true
console.log(div.__proto__.__proto__.__proto__.__proto__ === Node.prototype); // true
console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__ === EventTarget.prototype) // true
console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__ === Object.prototype) // true
console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__) // null
对象的赋值
function Person(name) {
this.name = name
}
Person.prototype.age = 18;
let p1 = new Person("zhangsan");
// 给对象赋值一个 age = 20属性
p1.age = 20;
console.log(p1.age); // 20
let p2 = new Person("lisi");
console.log(p2.age); // 18
console.log(p1); // name:"zhangsan" age:20
console.log(p2); // name:"lisi" __proto__ 能看到age:18
给实例对象的属性赋值,不会改变原型中的数据
只会给这个对象动态的添加属性:属性值