MDN——javascript——入门——第三章对象——对象.构造函数.原型链.继承——知识点总结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MDN——javascript——入门——第三章对象——对象.构造函数.原型链.继承——知识点总结相关的知识,希望对你有一定的参考价值。

对象Object

由属性property(变量)、方法method(函数)组成

 

var objectName = {

  member1Name : member1Value,

  member2Name : member2Value,

  member3Name : member3Value

}

member(成员)的值是任意的,

 

一个如上所示的对象被称之为对象的字面量(literal)——手动的写出对象的内容来创建一个对象。不同于从类实例化一个对象,我们会在后面学习这种方式。

 

访问对象成员

1.点表示法

对象的名字表现为一个命名空间(namespace),它必须写在第一位——当你想访问对象内部的属性或方法时,然后是一个点(.),紧接着是你想要访问的项目,标识可以是简单属性的名字(name),或者是数组属性的一个子元素,又或者是对象的方法调用。如下所示:

person.age

person.interests[1]

person.bio()

 

子命名空间

可以用一个对象来做另一个对象成员的值。例如将name成员

 

 

2.括号表示法

person.age

person.name.first

换成

person[‘age‘]

person[‘name‘][‘first‘]

这看起来很像访问一个数组的元素,从根本上来说是一回事儿,你使用了关联了值的名字,而不是索引去选择元素。难怪对象有时被称之为关联数组(associative array)——对象做了字符串到值的映射,而数组做的是数字到值的映射。(值对)

3.点表示法&括号表示法 差异

点表示法只能接受字面量的成员的名字,不接受变量作为名字。

括号表示法一个有用的地方是它不仅可以动态的去设置对象成员的值,还可以动态的去设置成员的名字。

比如说,我们想让用户能够在他们的数据里存储自己定义的值类型,通过两个input框来输入成员的名字和值,通过以下代码获取用户输入的值:

var myDataName = nameInput.value

var myDataValue = nameValue.value

我们可以这样把这个新的成员的名字和值加到person对象里:

person[myDataName] = myDataValue

 

 

 

 

设置对象成员

person.age = 45

person[‘name‘][‘last‘] = ‘Cratchit‘

也可以创建新成员

person[‘eyes‘] = ‘hazel‘

person.farewell = function() { alert("Bye everybody!") }

 

 

This

关键字"this"指向了当前代码运行时的对象

 

你一直在使用对象

当我们这样使用字符串的方法时:

myString.split(‘,‘);

你正在使用一个字符串实例上可用的方法

当你这样访问document对象时:

var myDiv = document.createElement(‘div‘);var myVideo = document.querySelector(‘video‘);

你正使用在任一个Document实例上可用的方法。

 

Oop面向对象

Instantiation实例化

当一个对象实例需要从类中创建出来时,类的构造函数就会运行来创建这个实例。这种创建对象实例的过程我们称之为实例化-实例对象被类实例化

 

 

 

Inherited:继承(子类继承父类):

 

 

 

从子类实例化:

 

 

 

 

 

构建函数和对象实例

有些人认为 javascript 不是真正的面向对象的语言,比如它没有像许多面向对象的语言一样有用于创建class类的声明。JavaScript 用一种称为构建函数的特殊函数来定义对象和它们的特征。构建函数非常有用,因为很多情况下你不知道实际需要多少个对象(实例)。构建函数提供了创建你所需对象(实例)的有效方法,将对象的数据和特征函数按需联结至相应对象。

不像“经典”的面向对象的语言,从构建函数创建的新实例的特征并非全盘复制,而是通过一个叫做原形链的参考链链接过去的(see Object prototypes),所以这并非真正的实例,严格的讲, Javascript 在对象间使用和其它语言不同的机制共享特征。

创建对象:

1.工厂模式:调用一个创建对象的函数

函数返回值是那个对象

可以传入参数

冗长

function createNewPerson(name) {

  var obj = {};

  obj.name = name;

  obj.greeting = function () {

    alert(‘Hi! I\‘m ‘ + this.name + ‘.‘);

  }

  return obj;}

var salva = createNewPerson(‘salva‘);

salva.name;

salva.greeting();

2.构造函数Constructorjs中的类

一个构建函数通常是大写字母开头,这样便于区分构建函数和普通函数。

使用this关键字

创建对象是要用new

可创建很多对象,对象有不同命名空间

更高效:实际上函数也是在类中定义的,而不是在对象实例中

原生构造函数:Object(),Array()等,自动存在于执行环境中

 

function Person(name) {

  this.name = name;

  this.greeting = function() {

    alert(‘Hi! I\‘m ‘ + this.name + ‘.‘);

  };}

var person1 = new Person(‘Bob‘);

var person2 = new Person(‘Sarah‘);

你现在看到页面上有两个对象,每一个保存在不同的命名空间里,当你访问它们的属性和方法时,你需要使用 person1或者 person2 来调用它们。尽管它们有着相同的 name 属性和 greeting() method 它们是各自独立的,所以相互的功能不会冲突。注意它们使用的是自己的 name 值,这也是使用 this 关键字的原因,它们使用的从实参传入形参的自己的值,而不是其它的什么值。

两个新的对象被创建:

{

  name : ‘Bob‘,

  greeting : function() {

    alert(‘Hi! I\‘m ‘ + this.name + ‘.‘);

  }}

{

  name : ‘Sarah‘,

  greeting : function() {

    alert(‘Hi! I\‘m ‘ + this.name + ‘.‘);

  }}

New之后经历四个步骤:

1创建对象

2将构造函数的作用域给对象(因此this指向这个对象)

3执行构造函数中的代码

4返回新对象

 

3.new Object()用字面量创建

var person1 = new Object();

person1.name = ‘Chris‘;

person1[‘age‘] = 38;

person1.greeting = function() {

  alert(‘Hi! I\‘m ‘ + this.name + ‘.‘);}

或者:

var person1 = new Object({

  name : ‘Chris‘,

  age : 38,

  greeting : function() {

    alert(‘Hi! I\‘m ‘ + this.name + ‘.‘);

  }});

4.调用Object()的create()方法

优点:基于现有的对象(person1)创建新对象(克隆)

缺点:浏览器兼容

var person2 = Object.create(person1);

person2.name

person2.greeting()

5.最常见的定义对象的方法:

原型  +  构造函数

原型用来放方法,构造函数用来放属性

因为

原型适合动态的添加方法,更灵活,省的在构造函数里面放那么多方法

原型不适合添加属性(全局变量不方便访问this)

 

 

 

 

 

 

 

 

 

 

 

对象原型

1.基于原型的语言

JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。

准确地说,这些属性和方法定义在 Object 的构造器函数之上,而非对象实例本身。

在传统的 OOP 中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。在 JavaScript 中并不如此复制——而是在对象实例和它的构造器之间建立一个连接(作为原型链中的一节),以后通过上溯原型链,在构造器中找到这些属性和方法

 

上溯原型链

例子:定义一个构造器函数:

function Person(first, last, age, gender, interests) {

  // 属性与方法定义

  };

然后创建一个对象实例:

var person1 = new Person(‘Bob‘, ‘Smith‘, 32, ‘male‘, [‘music‘, ‘skiing‘]);

 

调用 person1 的 “实际定义在 Object 上” 方法时,会发生什么?比如:

person1.valueOf()

1.浏览器首先检查,person1 对象是否具有可用的 valueOf() 方法。

2.如果没有,则浏览器检查 person1 对象的原型对象(即 Person)是否具有可用的 valueof() 方法。

3.如果也没有,则浏览器检查 Person() 构造器的原型对象(即 Object)是否具有可用的 valueOf() 方法。Object 具有这个方法,于是该方法被调用,

 

即:

方法和属性没有被复制到原型链中的其他对象——它们只是通过前述的“上溯原型链”的方式访问。

 

 

注意:没有正式的方法用于直接访问一个对象的原型对象——原型链中的“连接”被定义在一个内部属性中,在 JavaScript 语言标准中用 [[prototype]] 表示(参见 ECMAScript)。然而,大多数现代浏览器还是提供了一个名为 __proto__ (前后各有2个下划线)的属性,其包含了对象的原型。

你可以尝试输入 person1.__proto__ person1.__proto__.__proto__,看看代码中的原型链是什么样的!

 

在截图中能看到person1的原型是一个对象,包含一个构造器函数Person,包含一个原型属性__proto__,属性值是Object,说明在原型链中Person的上一级原型是Object

 

 

 

 

 

 

2.构造函数(类)的prototype 属性:一个对象,在这个对象中定义所有能被继承的成员

 

 

JavaScript 控制台输入 "person1.",你会看到,浏览器将根据这个对象的可用的成员名称进行自动补全。

但实际上,Object有更多属性和方法,不止上面那些,为什么上面没有显示完呢??????????

因为

要被继承的属性和方法是定义在 prototype 属性之上的(你可以称之为子命名空间 (sub namespace) ),

——那些以 Object.prototype. 开头的属性,而非仅仅以 Object. 开头的属性,才会被继承。

prototype 属性的值是一个对象,存储着我们希望被原型链下游的对象继承的属性和方法。

 

 

Object.prototype.watch()Object.prototype.valueOf() 等等成员(可以在控制台数Object.prototype回车查到所有可被继承的Object的成员),适用于任何继承 Object() 的对象类型,包括使用构造器创建的新的对象实例。

Object.is()Object.keys(),以及其他不在 prototype 对象内的成员,不会被“对象实例”或“继承自 Object() 的对象类型”所继承。这些方法/属性仅能被 Object() 构造器自身使用。

 

 

 

 

 

 

3.create方法

 

function Person(first, last, age, gender, interests) {

  

  // 属性与方法定义

  

};

var person1=new Person(1,1,1,1,1,);

person2=Object.create(person1);

person2.__proto__;

 

按照MDN上说的,会返回:person1对象

但实际上,

chrome浏览器返回了Person对象

firefox浏览器返回了Object对象................................................??此处有疑问

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            

4.constructor属性

每个对象实例都有constructor属性,指向对象的构造器(构造函数)

控制台输:

function Person(first, last, age, gender, interests) {

  // 属性与方法定义

};

var person1=new Person(1,1,1,1,1,);

person2=Object.create(person1);

person1.constructor

person1.constructor

都会返回Person构造函数:

function Person(first, last, age, gender, interests) {

  // 属性与方法定义

}

 

**用一个对象的构造器属性创建另一个对象:

var person3 = new person1.constructor(‘Karen‘, ‘Stephenson‘, 26, ‘female‘, [‘playing drums‘, ‘mountain climbing‘]);

(因为构造器本质就是构造函数,可以通过括号调用,但和普通函数不同的是,要加new关键字)

 

**constructor还有其他的属性和方法,如获得某对象构造器的name,length(参数个数),constructor(哈哈constructorconstructor,返回Function构造器,因为构造器本身就是函数来的),

 

 

 

3.修改prototype属性,(动态更新原型链)

编写Person构造函数/构造器      

function Person(first, last, age, gender, interests) {

        this.name = {

          first,

          last

        };

        this.age = age;

        this.gender = gender;

        this.interests = interests;

        this.bio = function() {}

        this.greeting = function() {

          alert(‘Hi! I\‘m ‘ + this.name.first + ‘.‘);

        };

      };

//为构造器的prototype属性添加新的一个方法:

Person.prototype.farewell = function() {

  alert(this.name.first + ‘ has left the building. Bye for now!‘);

}

//控制台输

person1.farewell();

 

你会看到一条警告信息,其中还显示了构造器中定义的人名;这很有用。但更关键的是,整条继承链动态地更新了,任何由此构造器创建的对象实例都自动获得了这个方法。

 

这证明了先前描述的原型链模型。这种继承模型下,上游对象的方法不会复制到下游的对象实例中;下游对象本身虽然没有定义这些方法,但浏览器会通过上溯原型链、从上游对象中找到它们。这种继承模型提供了一个强大而可扩展的功能系统。

 

 

 

注意,给prototype增加属性一般是增加方法

或增加常属性(无法改变的属性constant property

 

因为

Person.prototype.fullName = this.name.first + ‘ ‘ + this.name.last;

这样加会出错,,,this指代的是当前执行环境的全局范围

 

而加方法

Person.prototype.farewell = function() {

  alert(this.name.first + ‘ has left the building. Bye for now!‘);}

这样就会成功,因为此时语句位于函数范围内,从而能够成功地转换为对象实例范围。(是对象调用这个方法)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

继承

之前说的prototype继承,是指对象实例能够访问:构造器中prototype属性中的方法。

这里说的继承是 构造器 构造器 的继承

 

1.

//Person构造器:

function Person(first, last, age, gender, interests) {

  this.name = {

    first,

    last

  };

  this.age = age;

  this.gender = gender;

  this.interests = interests;

};

//Teacher构造器:

function Teacher(first, last, age, gender, interests, subject) {

  Person.call(this, first, last, age, gender, interests);

  this.subject = subject;

}

Teacher构造器从Person构造器继承了所有属性,又新增了一个subject属性。

这样很**高效**

 

 

 

 

call()函数。基本上,这个函数允许你调用一个在这个文件里 别处定义的函数。第一个参数指明了在你运行这个函数时想对“this”指定的值,也就是说,你可以重新指定你调用的函数里所有“this”指向的对象。其他的变量指明了所有目标函数运行时接受的参数。

(调用 同文件 别的函数 ,能改变/指定 this指向的值 )

 

 

 

2.Person()继承prototype

上面的使用call的方法,让Teacher()继承到了Person()的所有属性,但是此时Teacher()只有一个空的prototype属性(本质也是一个对象),如何让Teacher()从Person()的原型属性里面继承方法呢?

Teacher.prototype = Object.create(Person.prototype);

哈!用老朋友**create**

create()函数来将Person.prototype这个对象克隆Teacher.prototype

因为prototype属性本质也是一个对象,所以可以用create的方法去克隆一个对象给Person.prototype

 

 

 

3.设置Teacher()的constructor

直接从Person()继承,使Teacher()prototype属性中的constructor属性指向Person()也就是说由Teacher()创建的对象实例,查这个对象的构造器时,返回的是Person(), 这是由我们生成Teacher()的方式导致的。(这篇 Stack Overflow post  https://stackoverflow.com/questions/8453887/why-is-it-necessary-to-set-the-prototype-constructor  文章会告诉你详细的原理)

 

 

这或许会成为很大的问题,所以我们需要将其正确设置——你可以回到源代码,在底下加上这一行代码来解决:

 

Teacher.prototype.constructor = Teacher;

 

以上是关于MDN——javascript——入门——第三章对象——对象.构造函数.原型链.继承——知识点总结的主要内容,如果未能解决你的问题,请参考以下文章

MDN——javascript——入门——第一章——知识点总结

MDN——javascript——入门——第二章——知识点总结

javascript课程大纲

只推荐一本 JavaScript 书,你推荐哪本?

关于JavaScript 的好书都有哪些

javascript MDN示例