对象进阶、原型-原型链

news/2024/11/24 2:03:01/

工厂方法创建对象

我们之前已经学习了如何创建一个对象,那我们要是想要创建多个对象又该怎么办?聪明的同学可能会说,直接在写几个对象不就好了吗?比如下边的代码:

var person1 = {name: "孙悟空",age: 18,sayName: function () {console.log(this.name);}
};var person2 = {name: "猪八戒",age: 19,sayName: function () {console.log(this.name);}
};var person3 = {name: "沙和尚",age: 20,sayName: function () {console.log(this.name);}
};console.log(person1);
console.log(person2);
console.log(person3);

的确,上述代码确实可以创建多个对象,但是,这样的解决方案真的好吗?对于少量对象可能使用,我们假设说,要用循环创建1000个对象,那你这种办法似乎就没用了,我们可以这么做,如下代码:

//使用工厂模式建立对象
function createdPerson(){   //创建对象var obj =new Object();//设置对象属性obj.name="孙悟空",obj.age="18",obj.sayName=function(){console.log(this.name);   };//返回对象return obj }var person1 = createPerson("孙悟空", 18);
var person2 = createPerson("猪八戒", 19);
var person3 = createPerson("沙和尚", 20);console.log(person1);
console.log(person2);
console.log(person3);

现在再看上述代码,发现好像已经完美的解决了创建多个对象的难题,那我们是不是可以用循环批量创建1000个对象了呢?那我们就来试试:

// 使用工厂模式创建对象
function createPerson(name, age) {// 创建新的对象var obj = new Object();// 设置对象属性obj.name = name;obj.age = age;// 设置对象方法obj.sayName = function () {console.log(this.name);};//返回新的对象return obj;
}for (var i = 1; i <= 1000; i++) {var person = createPerson("person" + i, 18);console.log(person);
}

这样我们就实现了批量创建对象的功能,至于对象的名称和年龄,我们可以通过名称数组和年龄数组来获取,但这并不是我们本小节的重点,我们就忽略了。 

用构造函数创建对象

你会发现我们所创建的对象类型都是Object,具体代码如下:

// 使用工厂模式创建对象
function createPerson(name, age) {// 创建新的对象var obj = new Object();// 设置对象属性obj.name = name;obj.age = age;// 设置对象方法obj.sayName = function () {console.log(this.name);};//返回新的对象return obj;
}for (var i = 1; i <= 1000; i++) {var person = createPerson("person" + i, 18);console.log(typeof person);
}

那这有问题吗?看起来有,看起来好像又没有,每创建一个都是对象,但是在实际生活中,人应该是一个确定的类别,属于人类,对象是一个笼统的称呼,万物皆对象,它并不能确切的指明当前对象是人类,那我们要是既想实现创建对象的功能,同时又能明确所创建出来的对象是人类,那么似乎问题就得到了解决,这就用到了构造函数,每一个构造函数你都可以理解为一个类别,用构造函数所创建的对象我们也成为类的实例,那我们来看看是如何做的:
 

// 使用构造函数来创建对象
function Person(name, age) {// 设置对象的属性this.name = name;this.age = age;// 设置对象的方法this.sayName = function () {console.log(this.name);};
}var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);console.log(person1);
console.log(person2);
console.log(person3);

那这构造函数到底是什么呢?我来解释一下:

构造函数:构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写,构造函数和普通函数的还有一个区别就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。

那构造函数是怎么执行创建对象的过程呢?我再来解释一下:

调用构造函数,它会立刻创建一个新的对象
将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
逐行执行函数中的代码
将新建的对象作为返回值返回
你会发现构造函数有点类似工厂方法,但是它创建对象和返回对象都给我们隐藏了,使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。我们将通过一个构造函数创建的对象,称为是该类的实例。

现在,this又出现了一种新的情况,为了不让大家混淆,我再来梳理一下:

当以函数的形式调用时,this是window
当以方法的形式调用时,谁调用方法this就是谁
当以构造函数的形式调用时,this就是新创建的那个对象
我们可以使用 instanceof 运算符检查一个对象是否是一个类的实例,它返回true或false

语法格式:

对象 instanceof 构造函数
console.log(person1 instanceof Person);

原型
使用构造函数的方式进行创建对象,但是,它还是存在一个问题,那就是,你会发现,每一个对象的属性不一样这是一定的,但是它的方法似乎好像是一样的,如果我创建1000个对象,那岂不是内存中就有1000个相同的方法,那要是有10000个,那对内存的浪费可不是一点半点的,我们有没有什么好的办法解决,没错,我们可以把函数抽取出来,作为全局函数,在构造函数中直接引用就可以了,上代码:

// 使用构造函数来创建对象
function Person(name, age) {// 设置对象的属性this.name = name;this.age = age;// 设置对象的方法this.sayName = sayName
}// 抽取方法为全局函数
function sayName() {console.log(this.name);
}var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);person1.sayName();
person2.sayName();
person3.sayName();

但是,在全局作用域中定义函数却不是一个好的办法,为什么呢?因为,如果要是涉及到多人协作开发一个项目,别人也有可能叫sayName这个方法,这样在工程合并的时候就会导致一系列的问题,污染全局作用域,那该怎么办呢?有没有一种方法,我只在Person这个类的全局对象中添加一个函数,然后在类中引用?答案肯定是有的,这就需要原型对象了,我们先看看怎么做的,然后在详细讲解原型对象。

// 使用构造函数来创建对象
function Person(name, age) {// 设置对象的属性this.name = name;this.age = age;
}// 在Person类的原型对象中添加方法
Person.prototype.sayName = function() {console.log(this.name);
};var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);person1.sayName();
person2.sayName();
person3.sayName();

那原型(prototype)到底是什么呢?

我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象,即显式原型,原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

如果函数作为普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__(隐式原型)来访问该属性。当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。

以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。


原型链

访问一个对象的属性时,先在自身属性中查找,找到返回, 如果没有,再沿着__proto__这条链向上查找,找到返回,如果最终没找到,返回undefined,这就是原型链,又称隐式原型链,它的作用就是查找对象的属性(方法)。

我们使用一张图来梳理一下上一节原型案例的代码:

// 使用构造函数来创建对象
function Person(name, age) {// 设置对象的属性this.name = name;this.age = age;
}Person.prototype.height =10;
var a = new Person();a._proto_=== Person.prototype  // true
Person===Person.protype.constructor  // true
Object.getPrototype(person1) === Person.prototype //true

当我们使用 a 的

 注意:Object对象是所有对象的祖宗,Object的原型对象指向为null,也就是没有原型对象


http://www.ppmy.cn/news/278332.html

相关文章

qt 乱码

init() { // 根据配置加载默认语言系统 // 解决中文乱码问题 QTextCodec *codec QTextCodec::codecForName("system"); QTextCodec::setCodecForLocale(codec); } 设置 /execution-charset:utf-8

国有行面试的8种误区回答(上)

从各地区面试情况来看&#xff0c;国有行面试依旧是自我介绍半结构化无领导群面&#xff0c;辅之辩论赛等形式。 从多数地区和岗位面试情况看&#xff0c;我们知道自我介绍几乎是必考面试题&#xff0c;甚至不少地区一面只让作一次自我介绍就结束&#xff0c;可见自我介绍对面试…

JavaSE08_面向对象之继承

JavaSE-08 【继承】 第一章 继承 1.1 继承介绍 继承的概念&#xff1a; 让类与类之间产生关系&#xff08;子父类关系&#xff09;&#xff0c;子类可以直接使用父类中非私有的成员继承是多态的前提&#xff0c;如果没有继承&#xff0c;就没有多态继承主要解决的问题就是共…

一颗明亮的火球从天空划过

我不知道&#xff0c;估计也没有几个普通人能够说清楚。但有一个真实的事件&#xff0c;它被普遍认为是地球存在高级文明&#xff0c;或者地球被外星人保护的最好证明——俄罗斯陨石被击碎事件。 2013年2月15日&#xff0c;俄罗斯南部的车里雅宾斯克&#xff0c;一颗明亮的火球…

河北山东多地出现 UFO专家称这是一颗火流星。

十月二十五日晚上九点半&#xff0c;河北石家庄某高校的一位大学生在操场上看见一只不明物体从空中掠过&#xff0c;恰好有同学用手机拍下了这一画面。画面中&#xff0c;那架 UFO的速度很快&#xff0c;在它的身后留下了一道诡异的绿光&#xff0c;而在它的身后&#xff0c;还…

单词Uranolite陨石uranolite英语

陨石(uranolite)是指来自地球以外太阳系其他天体的碎片&#xff0c;绝大多数来自位于火星和木星之间的小行星&#xff0c;少数来自月球(40块&#xff09;和火星&#xff08;40块&#xff09;。全世界已收集到4万多块陨石样品&#xff0c;石陨石主要成分是硅酸盐。 它们大致可分…

2D游戏——4月11日

飞行道具1 飞行道具的设计 飞行道具1概述道具介绍类图代码飞行道具父类火球岩浆保护性火球陨石 概述 根据设定&#xff0c;我们的游戏有火、冰、风、雷4个属性&#xff0c;每一种主副元素的组合&#xff0c;都会有不同的技能。根据团队的讨论和设计&#xff0c;这些技能中有的…

unity生涯的开始——太空飞船小游戏制作

初识unity做的一个小 demo 1、飞船等场景的设置 场景布局&#xff1a;把灯光放到合适的位置&#xff0c;摄像机拉到灯光上方&#xff0c;在scene里面新建一个quad作为背景&#xff0c;给它贴上材质图&#xff0c;把飞船player拖到场景中&#xff0c;调整位置&#xff0c;在飞船…