一、对象创建的几种方式:字面量创建、new Object()、构造函数、构造函数+原型链、class方式
1,字面量创建对象
var obj= {name:'CSDN',age:10}
2,使用new Object()方法创建
var obj = new Object({name:'feel',type:2})
3,工厂模式(封装、特点:能够批量生产)
function dog(type,gender){
let obj ={type , gender}
//或者 let obj1 = new Object({type , gender} )
return obj
}
let dog1 = dog("哈士奇","公")
let dog2 = dog("柯基","母")
console.log(dog1,dog2);
//{type: '哈士奇', gender: '公'} {type: '柯基', gender: '母'}
4,构造函数(javascript定义的方法)
// 自定义构造函数 首字母大写
function Preson(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
// 创建对象的时候必须使用new
let p1 = new Preson("小黑", 10, "男")
创建实例的时候 ,必须用new关键字。(new关键字创建的时候过程在结尾)
5,构造函数+原型链,这种比较完善
function Person(name,age){
this.name = name
this.age = age
this.setAge =function(){
console.log('age',this.age);
}
this.testArr = [1,2,3]
}
//Person.prototype.testArr = [1,2,3]
Person.prototype.setName = function(name){ //在自定义构造函数的原型对象中添加方法
console.log(this.name);
}
let obj1 = new Person("小明",12,'画画')
let obj2 = new Person("小李",13,'画画1')
大家可以打印一下obj1.setName == obj2.setName的结果值
以及obj1.setAge == obj2.setAge的结果值,他们之间的区别,
以及第七行的数组如果放在第9行的Person的原型上,随便一个实例修改的时候,其他实例这个数组的值。可以自行打印一下。
结果:
两个实例,创建完成时具有Person身上的属性和方法,也就是有name,age,setAge这些,两个实例创建完成后在内存空间开辟两个问题,分别放obj1和obj2,他们有各自的steAge方法,但是,Person.prototype原型上面的setName方法,并没有创建到实例本身上,所以实例本身是没有这个方法的。通过原型链的知识,可以知道,实例本身没有这个方法,会向上一层,一层一层的寻找,直到最后。所以实例往上寻找,到Person的原型上,找到这个方法。意思就是实例调用的不是自身的方法,而是构造函数原型上的方法。
如果,我们把第七行注释,把第九行放开,使用一个实例向数组push内容。打印看
实例2身上的属性也发生了改变。所以,有引用属性的时候,必须把引用属性放在构造函数本身,不能放在原型上。
6,class模式(代码结构清晰)、constructor 放实例成员
定义方法前加 static为静态方法 里面的this指向构造函数
直接定义的为原型对象里面的方法 this指向实例
class Person {
// 构造函数
constructor(name, age) {
// 实例成员
this.name = name
this.age = age
}
// 原型成员 (在其prototype对象里)
eat() {
// this 指向式实例
console.log(this.name + "爱睡觉");
}
// 静态成员
static shows() {
// 静态成员this指向构造函数Person
console.log(this.name + "爱吃饭");
}
}
// 实例化
var p = new Person("小白", 10)
console.log(p); // Person {name: '小白', age: 10}
p.eat() // 小白爱睡觉
Person.shows() //Person爱吃饭
//静态成员只能通过构造函数调用 static方法,就是可以创建变量的标识,只能自己用,实例用不了。
二、对象之间的继承
js中对象之间的继承有7种方式:原型链继承、构造函数继承、组合继承、原型式继承、寄生继承、寄生组合式继承、以及ES6中的class中的继承
1,原型链继承
function Animal(){
this.name = 'animal'
}
Animal.prototype.getAnimalName = function (){
console.log(this.name + 'getAnimalName');
}
//子类
function Cat(){
this.name = 'cat'
}
// Cat继承Animal,将Animal的实例对象赋值给Cat的原型对象,
Cat.prototype = new Animal()
Cat.prototype.getCatName = function (){
console.log(this.name + 'getCatName');
}
console.log(Cat.prototype.__proto__ === Animal.prototype);//true
// 给Cat的原型对象上声明一个方法
// 在使用原型链继承的时候,要在继承之后再去原型对象上定义自己所需要的属性和方法,因为在继承之前在原型对象上定义方法和属性将会被覆盖
// 创建实例对象cat1
var cat1 = new Cat()
console.log(cat1);//{name:'cat'}
// 调用自己原型对象上定义的方法
cat1.getCatName()//catgetCatName
// 调用继承Animal原型对象上的方法
cat1.getAnimalName()//catgetAnimalName
特点:
让新实例的原型等于父类的实例
缺点:
新实例无法向父类构造函数传参。
继承单一
2,构造函数继承
使用 .call(this)方法将父类构造函数引入子类函数
//父类
function Person(name, age) {
// 父类身上的属性 和 方法
this.name = name
this.age = age
this.play = [1, 2, 3] //引用类型
this.setName = function () {
console.log(name)
}
}
//子类
function Student(name, age) {
Person.call(this, 'jimi',12)//相当于this.Person(name,age)
this.name = name
this.setScore = function () {}
}
var s1 = new Student('Tom', 20)
console.log(s1)
// Student {name: 'Tom', age: 12, play: Array(3), setName: ƒ, setScore: ƒ}
特点:
实现多个构造函数属性的继承(call多个)
实现子类向父类传参
缺点:
只能继承父类实例上的属性和方法,不能继承原型上的属性和方法
每个实例都有父类构造函数的副本,臃肿
3,组合继承
是原型继承和构造函数继承两种的结合
function Person(name, age) {
// 父类身上的属性 和 方法
this.name = name
this.age = age
this.play = [1, 2, 3]
this.setName = function () {}
}
// 父类原型链上的方法
Person.prototype.setAge = function () {
console.log('111');
}
//子类
function Student(name, age, price) {
Person.call(this, name, age)//相当于this.Person(name,age) 构造函数模式
this.price = price
this.setScore = function () {}
}
Student.prototype = new Person() //原型链模式
Student.prototype.constructor = Student() //原型对象私有化//因为此时子类的构造函数指
//向不对
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000)
var s2 = new Student('Jack', 22, 14000)
console.log(s1,s2)
优点:
可以继承实例属性/方法,也可以继承原型属性/方法
不存在引用属性共享问题
可传参
函数可复用
缺点:
调用了两次父类构造函数,生成了两份实例
4,原型式继承
使用Object.create()方法
//父类
function Person(name, age) {
// 父类身上的属性 和 方法
this.name = name
this.age = age
}
var testPer = new Person();
let Student = Object.create(testPer)
//console.log(Student) //Person{}
//相当于:
//封装一个容器,输入对象和承载继承的原型
function content(Obj){
function F(){}
F.prototype = Obj 继承传入的参数
return new F() //返回函数对象
}
//用法:
var test = new Person() //用他的实例,当做那个函数的参数,返回一个对象
var Student = content(test)
//console.log(Student)
//这里的打印和上面的那个打印效果是一样的。
特点:
封装一个函数,返回一个对象
缺点:
无法传递参数
多个实例的引用类型属性指向相同的内存,存在篡改的可能
5,寄生式继承
在原型上的基础上增强Object.create()的方法,最后返回一个对象
function createA(original) {
var clone = Object.create(original) //调用函数,创建一个对象
clone.sayHello = function () { //增强对象
console.log('hello');
}
return clone; //返回对象
}
// 使用:
var person12 = {
name: 'mashang',
}
var per = createA(person12)
console.log(per);//F{sayHello:f()}
per.sayHello();//hello
//相当于:
function content(obj){ //原型式继承
function F(){}
F.prototype = obj
return new F()
}
var Stu1 = new Person();
//继续套壳子,传递参数
function subObject(obj){
var sub = content(obj)
sub.name = 'tom'
return sub
}
var Stu2 = subObject(Stu1);// 传的参数,使用的函数,
//函数声明后,就成了可增添属性的对象
//console.log(typeof subObject) //function
//console.log(typeof Stu2) //object
//console.log(Stu2.name) //'tom',继承的sub属性
特点:
给原型式继承外面套了个壳子,可以增添一些方法
缺点:
没用到原型,无法实现复用,
6,寄生组合式继承
是将组合式和寄生式结合起来,是目前最成熟方法
//父类
function Person(name, age) {
// 父类身上的属性 和 方法
this.name = name
this.age = age
this.play = [1, 2, 3]
this.setName = function () {}
}
// 父类原型链上的方法
PersonN.prototype.setAge = function () {
console.log('111');
}
//子类
function Student(name, age, price) {
Person.call(this, name, age)//相当于this.Person(name,age)
this.price = price
this.setScore = function () {}
}
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student//修复构造函数指向的
var s1 = new Student('Tom', 20, 15000)
var s2 = new Student('Jack', 22, 14000)
console.log(s1,s2)
特点:
减少构造函数,减少性能开销
7,class实现继承
class Parent {
// 构造函数
constructor(name) {
this.name = name
}
// 原型方法
// 即 Person.prototype.getName = function() { }
getName() {
console.log("Person", this.name);
}
}
//子类
class ChildClass extends Parent {
//自身的 构造函数
constructor(name, age) {
//在子类的构造函数中 首先调用super()函数,继承父类属性方法
super(name)
this.age = age
}
getAge() {
console.log('Child', this.age);
}
}
const dog = new ChildClass('dog', 1)
dog.getAge() //Child 1
dog.getName() //调用父类方法 Person dog
特点
ES6非常完善的继承方法
结尾:
new关键字过程
例:let obj = new Object()
1,开辟一个内存空间,创建一个空对象
2,将实例的__proto__对象指向Object构造函数的prototype属性上
3,改变this指向从指向构造函数改变为指向改新对象
4,给实例添加属性方法
最后,返回这个继承而来的对象。