一、对象定义的三种方式
一、对象的构成、
对象是由它本身和它的原型公共构成的, 对象的原型是 proto (也是一个对象)
二、原型链:
对象或构造函数由本身和原型构成,对象或构造函数也是一个对象,由本身和原型构成,这样一直往后沿深,形成原型链。
二、定义对象键和键值
分为直接定义、追加定义
var obj2 = new Object();
// 添加对像内容
obj2.name = "张三";
obj2.age = 20;
console.log(obj2);
// 覆盖定义 obj2对象
obj2 = {
height:"178cm",
hobby:function(){
console.log("喜欢篮球");
}
}
console.log(obj2);
三、对象和JSON之间的转换
JSON键名一定要加 "",键值没有函数,规范 :引号使用--外单内双
var JSON = '{"name":"张三","age":"19"}';
var json1 = JSON.stringify(obj2); // 对象转json串(字符串化)
var newObj = JSON.parse(json1); // json数据转换成对象(解析)
四、传值和传址问题
1、传值,
var a = 10;
var b = a;
b = 5;
console.log(a,b); // 10 5 。不会相互影响
2、传址
var obj1 = {
a:10
}
var obj2 = obj1;
obj2.a = 5;
console.log(obj1.a,obj2.a); // 5 5
// 数组传址,数组之间相互影响
var arr1 = [1,2,3,4,5];
var arr2 = arr1;
arr2[1] = 10;
console.log(arr1[1]); // 10
解决传址问题:
1、深拷贝 -- 将数组转为JSON串,再转为数组
var arr1 = [1,2,3,4,5];
var arr2 = JSON.parse(JSON.stringify(arr1));
arr2[1] = 10;
console.log(arr1); // 2
2、使用空的构造函数,作为两个函数原型的桥梁。
解决方案: 空的构造函数,作为父级、子级的传递桥梁。
function People(sex,eat){
this.sex = sex;
this.eat = eat;
}
People.prototype.actions = () => {
console.log('People === actionsFn')
}
function Student(sex,eat){
this.sex = sex;
this.eat = eat;
}
Student.prototype.actions = () => {
console.log('Student === actions')
}
------------------------------
Student.prototype = People.prototype;
------------------------------
++++++++++++++++++++++++++++++
function Link(){
Link.prototype = People.prototype;
}
Student.prototype = new Link;
+++++++++++++++++++++++++++++++
Student.prototype.actions = () => {
console.log('Stundent === New');
}
const newPeo = new People('女','水果');
const newStu = new Student('man','dontknow');
newPeo.actions();
newStu.actions();
五、构造函数
构造函数(类似类的概念); 约定俗成:首字母大写
1、定义构造函数
function People(eat,age){
this.eat = eat;
this.age = age;
this.action = function(){
console.log("吃饭,睡觉");
}
}
2、构造函数的原型
- 构造函数由本身和原型prototype构成;
- 原型链:原型是一个构造函数,又由本身和构造函数构成,一直往后延深,形成原型链。
- 一般属性写在构造函数里,方法写在原型上,目的是:便于维护
- 在写方法时,直接:People.prototype = function({}),会覆盖原有的原型
People.prototype.action = function(){
console.log(this); // 实列化是谁,this指向谁。
console.log(this.eat);
console.log("我是爸爸");
}
3、通过原型找构造函数
console.log(People.prototype.constructor);
4、调用构造函数
关键字 new:实列化构造函数。抽象具体化,将类变为对象
newPeople = new People("米饭",18);
console.log(newPeople.eat);
newPeople.action();
5、关键字new具体做的三件事:
创建空的对象
var newPeople = new Object();
将构造函数的this指向改变成空对象 (3种写法:call/apply/bind)
People.call(newPeople,"米饭",18);
// People.apply(obj,["米饭",18])
// People.bind(obj)("米饭",18);
将构造函数的原型赋给对象原型
newPeople.__proto__ = People.prototype; // 构造函数的原型 赋给 实列化原型
6、构造函数的原型
console.log(People.prototype === newPeople.__proto__); // true , 只是表现形式不一样
7、构造函数的 -- 继承
1、改变this指向
function Father(eat,age){
this.eat = eat;
this.age = age;
this.fath = function(){
console.log("我是爸爸");
}
}
function Sons(eat,age){
Father.call(this,eat,age);
this.sonFn = function(){
console.log("我是儿子");
}
}
2、原型的继承
// 不可以直接:Sons.prototype = father.prototype;
// -- 解决传址问题 !!!
// 1、定义一个空构造函数,其原型指向father原型,son原型指向其原型
function Center(){};
Center.prototype = Father.prototype;
Sons.prototype = new Center(); // 空构造函数只有原型
Father.prototype.typeFn = function(){
console.log("父级重写");
}
Sons.prototype.typeFn = function(){
console.log("子级重写");
}
var newSons = new Sons("苹果",10);
var newFather = new Father("苹果",10);
console.log(newSons.eat);
newSons.sonFn();
newSons.fath();
newFather.typeFn();
newSons.typeFn();
六、类的特性:封装、继承、多态、重载; [js:封装、继承]
1、封装 -- 公有、私有属性
function Person3(name){
// 1.1、私有属性
var name = name;
// 1.2、公有属性
this.height = "179cm";
// 1.3、get方法:通过公有方法,访问私有属性
this.get = function(){
return name;
}
// 1.4 set方法:设置私有属性
this.set = function(newname){
name = newname;
console.log(name);
}
}
newPerson3 = new Person3("三三");
console.log(newPerson3.name); // undefind:作用域问题,访问不到。只能在构造函数内部访问
console.log(newPerson3.get()); // 三三
newPerson3.set("四四"); // 四四
2、继承
1、利用 apply、call、bind 方法,改变this指向 实现继承
- 通过 apply、call、bind:继承父类 的 this ----> 只能继承实例的 方法、对象
- 父类公有对象、方法 都通过 this 递增,每 new 一个 实例都导致 this 内容的存储,耗费很大内存
function Student(name, age, grade){
// 将 this 指向 Person,达到继承 Person对象实例 的 方法、目的
Person.apply(this, arguments);
// Person.call(this, arguments);
// Person.bind(this)(arguments);
this.grade = grade;
}
function Person(name, age){
this.age = age;
this.name = name;
this.type = 'person';
}
Person.prototype.proFn = () => {
console.log('Person === proFn')
}
var student = new Student('sname', 'sage', 'sgrade');
console.log(student);
2、空对象作为中介
- 只能通过 Prototype 来传递
- 优势:
少 new 很多的 this 构造函数,节省内存
子类 prototype 改变,不影响父类
function Student(name , age, grade){
this.grade = grade;
}
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.type = 'preson';
function Extend(Child, Partent){
var F = function(){}; // 空对象
F.prototype = Partent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child; // 将 Child 赋给 当前对象的构造函数 ( 防止继承链的紊乱,需手动把构造器指回Child )
Child.uber = Partent.prototype; // Child 的 Uber 属性,直接指向 Partent 的 prototype (只是为了实现继承的完备性,纯属备用性质)
}
Extend(Student, Person);
var student = new Student('sname', 'sage', 'sgrade');
Student.prototype.subType = 'ssubType';
console.log('student +++', student.type)
console.log('student +++', student.subType)
3、拷贝继承
function Student(name, age, grade){
this.grade = grade;
}
function Person(name, age){
this.name = name;
this.age = age;
}
function Extend(Child, Partent){
var p = Partent.prototype;
var c = Child.prototype;
for (const i in p) { // 遍历拷贝赋值
c[i] = p[i];
}
c.uber = p;
}
Person.prototype.type = 'person';
Extend(Student, Person);
Student.prototype.subType = 'sperson';
var student = new Student('sname', 'sage', 'sgrade');
console.log(student)
七、工厂模式
function Factory(height){
//obj人类
var obj = {};
obj.name = 20;
obj.name = "张三";
obj.height = height;
obj.hobby = function(){
console.log("我喜欢篮球");
}
return obj;
}
var newFactory = Factory("162");
console.log(newFactory.height);
newFactory.hobby();