zoukankan      html  css  js  c++  java
  • 面向对象的程序设计 <step 2 ~ 创建对象>

    大家好,我是苏日俪格,本文是面向对象的第二部分,纯属个人理解,有哪里不对的地方请在评论区指出,大家一起学习共同进步。

    创建对象

    在面试中,经常会被问到创建对象都有哪些方式,在创建单个对象的时候通常就用对象字面量,多个对象就用工厂模式、构造函数、原型模式和构造函数原型的混合模式
    下面来逐个介绍一下:

    • 对象字面量

    栗子如下:

    let Person = {
    	name: '苏日俪格',
    	age: 24,
    	job: '前端开发'
    }
    console.log(Person)	// {name: "苏日俪格", age: 24, job: "前端开发"}
    

    优点:通俗易懂,人人都会的一种简单的方法
    缺点:只适用于创建单个对象,用同一个接口创建多个对象的话,就会有很多的冗余代码,为了解决这个缺点,我们使用工厂模式

    • 工厂模式

    栗子如下:

    function createPerson(name, age, job){
    	let obj = new Object();
    	obj.name = name;
    	obj.age = age;
    	obj.job = job;
    	obj.show = function(){
    		console.log(`姓名:${obj.name}, 年龄:${obj.age}, 工作:${obj.job}`);
    	}
    	return obj;
    }
    let person1 = createPerson('苏日俪格', 24, '前端开发');
    person1.show();	// 姓名:苏日俪格, 年龄:24, 工作:前端开发
    let person2 = createPerson('赵云', 27, '救阿斗');
    person2.show();	// 姓名:赵云, 年龄:27, 工作:救阿斗
    

    优点:封装了一个函数解决了代码冗余的问题
    缺点:无法明确创建的对象的类型,为了解决这个缺点,我们使用构造函数

    • 构造函数

    栗子如下:

    function Person(name, age, job){
    	this.name = name;
    	this.age = age;
    	this.job = job;
    	this.show = function(){
    		console.log(`姓名:${this.name}, 年龄:${this.age}, 工作:${this.job}`);
    	}
    }
    let person1 = new Person('苏日俪格', 24, '前端开发');
    person1.show();	// 姓名:苏日俪格, 年龄:24, 工作:前端开发
    let person2 = new Person('赵云', 27, '救阿斗');
    person2.show();	// 姓名:赵云, 年龄:27, 工作:救阿斗
    

    和工厂模式的不同之处:

    1. 没有显式的创建对象(new Object())
    2. 没有return
    3. 直接将属性和方法赋给了this对象
    4. Person是一个构造函数,首字母大写(这里注意,由于构造函数和普通函数的区别在于有无返回值,并不是大小写,小写也可以,但是为了语义化也算是行规,必须大写)

    优点:由于两个实例共享了show这个全局的方法,就解决了两个函数做一件事的问题
    缺点:如果定义了多个全局的函数,那么这个自定义的引用类型就丝毫灭有封装性可言了,而且每个方法都要在每个实例上重新创建一遍,为了解决这个缺点,我们使用原型模式

    • 原型模式

    栗子如下:

    function Person(){}
    Person.prototype.name = '苏日俪格';
    Person.prototype.age = 24;
    Person.prototype.job = '前端开发';
    Person.prototype.show = function(){
    	console.log(`姓名:${this.name}, 年龄:${this.age}, 工作:${this.job}`);
    }
    let person1 = new Person();
    person1.show();	// 姓名:苏日俪格, 年龄:24, 工作:前端开发
    let person2 = new Person();
    person2.show();	// 姓名:苏日俪格, 年龄:24, 工作:前端开发
    person2.name = '赵云';
    person2.age = 27;
    person2.job = '救阿斗';
    person2.show();	// 姓名:赵云, 年龄:27, 工作:救阿斗
    

    优点:可以让所有对象的实例共享它所包含的属性和方法,不用再从实例中重新定义信息,直接将信息放在原型对象中
    缺点:显而易见,所有实例都是共享的属性,但是实例一般会有自己单独的属性的,这种方法一般不用,那么最后一种就是结合了前面所有的缺点的一种方式,也是最让码农们认同的

    这个时候有些人就想了,重复写那么多代码,我们可以简写成这样的啊:

    function Person(){}
    Person.prototype = {
    	name: '苏日俪格',
    	age: 24,
    	job: '前端开发',
    	show: function(){
    		console.log(`姓名:${this.name}, 年龄:${this.age}, 工作:${this.job}`);
    	}
    }
    let person1 = new Person();
    person1.show();	// 姓名:苏日俪格, 年龄:24, 工作:前端开发
    let person2 = new Person();
    person2.show();	// 姓名:苏日俪格, 年龄:24, 工作:前端开发
    person2.name = '赵云';
    person2.age = 27;
    person2.job = '救阿斗';
    person2.show();	// 姓名:赵云, 年龄:27, 工作:救阿斗
    

    上面折中写法确实清晰了许多,但是这个是在原型模式的情况下,把构造函数的原型等于了以对象字面量的形式创建的对象,这个时候constructor属性就不再指向Person了,为了证实这一点来看一个小东西instanceof

    instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。

    这句话是什么意思呢?
    来看一个语法:object instanceof constructor
    就是用instanceof来检测一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上
    字面理解: constructor.prototype 是否存在于object 的原型链上
    在上面加上这四行代码:

    console.log(person1 instanceof Person)	// true
    console.log(person1 instanceof Object)	// true
    console.log(person1.constructor == Person)	// false
    console.log(person1.constructor == Object)	// true
    

    很明显,我们要的效果出来了,实例的构造函数已经由Person指向了Object,这个时候需要在代码里加上constructor的指向

    Person.prototype = {
        constructor: Person,
    	name: '苏日俪格',
    	age: 24,
    	job: '前端开发',
    	show: function(){
    		console.log(`姓名:${this.name}, 年龄:${this.age}, 工作:${this.job}`);
    	}
    }
    
    • 构造函数原型的混合模式

    栗子如下:

    function Person(name, age, job){
    	this.name = name;
    	this.age = age;
    	this.job = job;
    }
    Person.prototype = {
    	constructor: Person,
    	show: function(){
    		console.log(`姓名:${this.name}, 年龄:${this.age}, 工作:${this.job}`);
    	}
    };
    let person1 = new Person('苏日俪格', 24, '前端开发');
    person1.show();
    let person2 = new Person('赵云', 27, '救阿斗');
    person2.show();
    
    console.log(person1.name == person2.name)	// false
    console.log(person1.show == person2.show)	// true
    

    做了个实验,看看两个实例到底是怎样的,共享的方法得到了验证,两者的属性并不是共享的,因为在创建实例的同时,系统开辟了单独的内存给它,每个实例也都会给自己的属性创建一个副本,所以他们之前是互不影响的

    优点:可以通过构造函数模式来定义实例所需要的属性,用原型来定义实例共享的属性和方法(谨记:本身自带的属性的权重始终高于原型定义的属性),分工明确

    对象创建好了,关键的地方来了,在对象继承之前先要搞明白__proto__和prototype的关系,这个懂了,就可以玩原型链继承了_
    本文的所有内容均是一字一句敲上去的,希望大家阅读完本文可以有所收获,因为能力有限,掌握的知识也是不够全面,欢迎大家提出来一起分享!谢谢O(∩_∩)O~

    我的简书:https://www.jianshu.com/u/72f239ec5d03
    等一下( •́ .̫ •̀ ),我还有最后一句话:
    我爱你,
    为了寻找你 ,
    我搬进鸟的眼睛,
    经常盯着路过的风 ,
    也忘了听猎人的枪声 ,
    再见...

  • 相关阅读:
    Linux命令-read
    pytest8-skip与xfail
    python模块-contextlib
    python中eval的使用
    python学习-面向对象(六)
    java学习-IDEA运行java程序报错
    nginx 使用SSL证书 代理https
    BC30560: “ExtensionAttribute”在命名空间“System.Runtime.CompilerServices”中不明确。
    nginx 代理相对路径使用不起
    nginx 解决不同web服务器 在同一服务器使用80端口问题
  • 原文地址:https://www.cnblogs.com/yufy/p/9354995.html
Copyright © 2011-2022 走看看