1、面向对象
创建对象:
a.使用new
b.使用字面量形式 : let obj = {name: ‘chen’}
1.1、属性描述符
可以精准的添加和修改对象的属性,需要使用Object.defindProperty
var obj = {
name: 'chen',
age: 12,
};
// 属性描述符是一个对象. 返回的是同一个对象
var newObj = Object.defineProperty(obj, 'height', {
value: 1.8
})
console.log(obj) //{ name: 'chen', age: 12 }
console.log(newObj) //{ name: 'chen', age:12}
console.log(obj === newObj) // true
// 因为他是不可枚举的,但是我们obj.height是可以拿到的
console.log(obj.height); // 1.8
a、数据属性描述符
// 这种方式定义,name和age虽然没有使用属性描述符来定义,但是他们也是具备特性的
// value: 就是赋值的value
// configurable: ture
var obj = {
name: "chen",
age: 23,
};
// 数据属性描述符, 默认值都是false,value:undefined
Object.defineProperty(obj, "address", {
value: "深圳",
// configurable: false:该属性不可删除,也不可修改或者重新定义属性描述符
configurable: false,
});
delete obj.name;
console.log(obj);
delete obj.address;
console.log(obj, obj.address); //{ age: 23 } 深圳
console.log('----------------------------------------------------')
var obj2 = {
name: "chen",
age: 23,
};
Object.defineProperty(obj2, "address", {
value: "深圳",
configurable: false,
enumerable: true,
// 是否可以修改值
writable: false,
});
// 测试enumerable的作用
console.log(obj2);
for (const key in obj2) {
console.log(key);
}
console.log(Object.keys(obj2));
obj2.address = '北京'
console.log(obj2);
b、存取属性描述符
var obj = {
name: "chen",
age: 23,
_address: '北京市'
};
// 有get、set这种就叫存取属性描述符
Object.defineProperty(obj, "address", {
configurable: true,
enumerable: true,
// value跟get不能共存
// value: "上海",
// 1、属性不想暴露可以使用这种, 隐藏私有属性
// 2、如果我们希望截获一个属性访问和设置值的过程时
get: function () {
return this._address;
},
// writable跟set也不能共存
// writable: true,
set: function (value) {
this._address = value;
},
});
console.log(obj.address);
obj.address = '广州'
console.log(obj.address);
2、创建对象
2. 1、new操作符的作用
2. 2、创建对象方案-构造函数(方案1)
// 规范: 构造函数的首字母大写
function Person(name, age) {
// this 是指向我们创建出来的对象的
this.name = name;
this.age = age;
this.eating = function () {
console.log(this.name + "正在吃饭");
};
}
//批量创建对象
var p1 = new Person("张三", 23);
var p2 = new Person("张三", 23);
console.log(p1);
console.log('---');
console.log(p1.eating === p2.eating); // false
//es6新语法
//class Person{}
2. 3、创建对象-构造函数的缺点分析
如果属性是函数的话,会创建相同的对象,没有必要,我们需要用原型优化
function foo() {
function bar() {}
return bar;
}
// foo返回一个函数对象,但是每次都是不同的对象。
var fn1 = foo();
var fn2 = foo();
// 用变量接受,产生了两个函数对象
console.log(fn1 === fn2);
2.4、对象的原型的理解
每个对象都是有原型的
//1、我们每个对象中都有一个[[prototype]], 这个属性可以称之为对象的原型(隐式原型)
var obj = { name: "chen" }; //[[prototype]]
var info = {}; //[[prototype]]
// 早期的ECMA是没有规范如何去查看[[prototype]],
// 给对象中提供了一个属性,可以让我们查看一下这个原型对象(浏览器提供)
// __proto__
console.log(obj.__proto__); //[Object: null prototype] {}
let obj1 = {
name: "fu",
// 这么定义,相当于下面还有一行代码 __proto__: {}
};
// 开发中是不推荐使用__proto__的 //obj.__proto__拿到的我们称为隐式原型
console.log(Object.getPrototypeOf(obj)); //拿到的我们称为隐式原型
// 2、原型的作用: 当我们从一个对象中获取某一个属性时,它会触发[[get]]操作
// a.在当前对象中查找对应的属性,如果找到就直接使用
// b.如果没有找到,那么会沿着它的原型链去查找[[prototype]]
console.log(obj.age);
2.5、函数的原型的理解
function foo() {}
//函数也是一个对象,也是有隐式原型的
console.log(foo.__proto__); //函数作为对象来说,他也是有[[prototype]]隐式原型的
// 函数因为是一个函数, 所以它还会多出了一个显示原型属性:prototype
console.log(foo.prototype);
// foo.prototype 显示原型的作用:(对象的隐式原型__proto__ -> 指向 构造函数的显示原型[[prototype]])
// 在我们使用new的时候,这个对象内部的[[prototype]]属性会被赋值为该构造函数的[[prototype]]属性
var f1 = new foo();
var f2 = new foo();
f1.__proto__ === foo.prototype;
f2.__proto__ === foo.prototype;
a、创建对象的内存表现
b、Person构造函数原型内存图
function Person(){
}
var p1 = new Person();
var p2 = new Person();
p1.__proto__.name = 'fnCu'
console.log(p1.name);
// 我们也可以这么添加name
Person.prototype.name = 'fuer'
console.log(p1.name);
2.6、函数原型上的属性
2.7、创建对象方案 - 原型和构造函数结合(方案2)
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.eating = function () {
// this 判定的是调用的那个对象,比如p1; this是动态绑定的,运行的时候绑定的
console.log(this.name + "吃东西");
};
var p1 = new Person("chen", 12);
var p2 = new Person("fuyi", 23);
console.log(p1.eating() === p2.eating()) //true
3、原型链 和 继承
3.1、原型链的理解
3.2、Object的原型的理解
var obj = {
name: 'chen'
};
console.log(obj.address);
// 字面对象obj的原型是
console.log(obj.__proto__); // [Object: null prototype] {}
console.log(obj.__proto__.__proto__); // null
[Object: null prototype] :已经是最顶层的原型了,它的原型属性指向的是null, 该对象上有很多默认的属性和方法
3.3、Person构造函数原型
function Person(){
}
console.log(Person.prototype); // {}
// {
// constructor: {
// value: [Function: Person],
// writable: true,
// enumerable: false,
// configurable: true
// }
// }
console.log(Object.getOwnPropertyDescriptors(Person.prototype));
console.log(Person.prototype.__proto__); //[Object: null prototype] {}
3.4、继承 - 原型链的继承方案(实现继承的方案1)
// 开发中不会实现下面的实现继承的所有代码, 下面的只是帮助理解的
// 父类: 公共属性和方法
function Person() {
this.name = "chen";
this.friend = [];
}
Person.prototype.eating = function () {
console.log(this.name + " eating~");
};
// 子类: 特有属性和方法
function Student() {
this.sno = 111;
}
// 现在我们使用原型方式继承
Student.prototype = new Person();
Student.prototype.studying = function () {
console.log(this.name + " studying");
};
var stu = new Student();
console.log(stu); // Student { sno: 111 }
console.log(stu.name);
stu.eating(); // stu中没有eating属性, 我们把undefined当成函数调用了,报错
// 原型链实现继承弊端
// 1.打印stu对象,继承的属性是看不到的
console.log(stu);
// 2.创建出两个stu
var stu1 = new Student();
var stu2 = new Student();
// 获取引用,修改引用中的值,会相互影响
// stu1.friend.push("fuyi");
console.log(stu1.friend); // [ 'fuyi' ]
// 我们发现, 下面的stu2也改了
console.log(stu2.friend); // [ 'fuyi' ]
// 但是,如果是下面这种方式,不会改的,只添加在stu1对象上
// 直接修改对象上的属性,是给本对象添加了一个新属性
stu1.name = 'chen'
console.log(stu1, stu2);
// 3.第三个弊端:在前面实现类的过程中都没有传参数
var stu3 = new Student('fuer', 23);
原型分析图:
原型链的继承方案弊端:
- 打印stu对象, 某些属性是看不见的(原型上的、继承的属性是打印不出来的)
3.5、继承 - 借用构造函数 继承方案(方案2:方案1的优化)
function Person(name, age, friend) {
// 这里的this就是传过来的studnet对象了
this.name = name;
this.age = age;
this.friend = friend;
}
Person.prototype.eating = function () {
console.log(this.name + " eating~");
};
function Student(name, age, friend) {
// 这个this是创建Student出来的对象, call 交给Person函数进行统一处理
Person.call(this, name, age, friend);
this.sno = 111;
}
Student.prototype = new Person();
Student.prototype.studying = function () {
console.log(this.name + " studying");
};
var stu = new Student('fuer', 23, ['fuyi']);
console.log(stu); //Person { name: 'fuer', age: 23, friend: [ 'fuyi' ], sno: 111 }
console.log(stu.name); //fuer
stu.eating();//fuer eating~
var stu1 = new Student('fuer', 23, ['fuyi']);
var stu2 = new Student('fuer', 23, ['zhangsan']);
console.log(stu1.friend); // [ 'fuyi' ]
console.log(stu2.friend); // [ 'fuyi' ]
stu1.name = "chen";
console.log(stu1, stu2); // 不一样
// 强调: 借用过程也是存在弊端的
// 弊端1:Person函数至少被调用了两次
// 弊端2:stu的原型对象上会多出一些属性,但是当前没有存在的必要
- 内存原型图
3.6、继承 - 父类原型直接赋值给子类 继承方案(方案3:针对方案2存在的问题)
// 不建议使用的
function Person(name, age, friend) {
this.name = name;
this.age = age;
this.friend = friend;
}
Person.prototype.eating = function () {
console.log(this.name + " eating~");
};
function Student(name, age, friend) {
Person.call(this, name, age, friend);
this.sno = 111;
}
// 父类原型直接赋值给子类
Student.prototype = Person.prototype;
// 弊端: 这styding会被加到Person.prototype中,是不对的,应该加到父类上的
Student.prototype.studying = function () {
console.log(this.name + " studying");
};
var stu = new Student('fuer', 23, ['fuyi']);
console.log(stu);
console.log(stu.name);
stu.eating();
// var stu1 = new Student('fuer', 23, ['fuyi']);
// var stu2 = new Student('fuer', 23, ['zhangsan']);
// console.log(stu1.friend); // [ 'fuyi' ]
// console.log(stu2.friend); // [ 'fuyi' ]
// stu1.name = "chen";
// console.log(stu1, stu2); // 不一样
弊端: 这styding会被加到Person.prototype中,是不对的,应该加到父类上的
3.7、继承 - 原型式继承函数-对象(方案4:针对方案3存在的问题)
var obj = {
name: "chen",
age: 23,
};
// 原型式继承函数:方式1
function createObject(o) {
var newObj = {};
// 设置newObj的原型是o
Object.setPrototypeOf(newObj, o);
return newObj;
}
function createObject2(o) {
function Fn() {}
Fn.prototype = o;
return new Fn();
}
// 原型式继承函数:方式2
var info = createObject2(obj);
console.log(info); //{}
console.log(info.__proto__); //{ name: 'chen', age: 23 }
注: 现在的版本中,有Object.create(obj), 它实现的功能跟我们方式一、方式二是一样的
3.8、继承 - 寄生式继承-对象
了解
var personObj = {
running: function() {
console.log('runing');
}
};
// var stuObj = Object.create(personObj);
// // 如果我们想在stuObj上扩展对象,需要.的方式
// stuObj.name = 'chen';
// stuObj.studing = function() {
// }
// 寄生式继承: 原型 + 工厂函数
function createStudent(name){
var stu = Object.create(personObj);
stu.name = name;
stu.styding = function(){
console.log('studying');
}
return stu;
}
var stu1 = Object.create('fuyi');
var stu2 = Object.create('fuer');
// var stu1 = Object.create(personObj);
// var stu2 = Object.create(personObj);
3.9、继承 - 寄生式组合式继承
// 继承函数封装
function inheritPrototype(SubType, SuperType) {
SubType.prototype = Object.create(SuperType.prototype);
Object.defineProperty(SubType.prototype, "constructor", {
enumerable: false,
writable: true,
configurable: true,
value: SubType,
});
}
function Person(name, age, friend) {
this.name = name;
this.age = age;
this.friend = friend;
}
Person.prototype.eating = function () {
console.log(this.name + " eating~");
};
function Student(name, age, friend, sno, score) {
Person.call(this, name, age, friend);
this.sno = sno;
this.score = score;
}
// Student.prototype = Object.create(Person.prototype);
// Object.defineProperty(Student.prototype, "constructor", {
// enumerable: false,
// writable: true,
// configurable: true,
// value: Student,
// });
// 01180D1D.png的代码不用,只需要下面的就行
inheritPrototype(Student, Person);
Student.prototype.studying = function () {
console.log(this.name + " studying");
};
var stu = new Student("chen", 23, ["fuyi"], 222, 30);
console.log(stu);
stu.studying();
stu.eating();