原型对象和原型链

一、内置一级构造函数Object(首字母大写)

1、普通函数和构造函数的特点

普通函数:

  1)、不建议使用new关键字调用,否则就成为构造函数的调用了;

  2)、可以用return语句返回值,默认返回值是undefined;

  3)、函数内部不建议使用this关键字,函数里面的this指向window,添加在this身上的属性是全局属性;

  4)、函数命名以驼峰方式,首字母小写;

       5)、每个普通函数都有一个原型(prototype对象),原型对象包含一个指向构造函数的引用,也有一个__proto__指明的继承关系。

构造函数:

  1)、用new关键字调用,否则就成为普通函数的调用了;

  2)、函数内部可以使用this关键字

    在构造函数内部,this指向的是构造出的新对象。用this定义的变量或函数/方法,就是实例变量或实例函数/方法。需要用实例才能访问到;

  3)、不用return返回值,有默认的返回值,也就是当前实例

         当然,我们也可以让构造函数像普通函数一样有返回值,但是返回值必须是引用类型对象,不能是值类型,如:null、undefined、boolean、number、string,否则这个返回值无效,依然返回构造的实例(this指向该实例)。例如可以返回{a: 'A', b: 'B'}等等;

  4)、函数命名建议首字母大写,与普通函数区分开

        不是强制性的,但是命名规范。

  5)、每个构造函数都有一个原型(prototype对象),原型对象包含一个指向构造函数的引用,也有一个__proto__指明的继承关系。

注意:普通函数和构造函数是没有界限,不是由命名决定的,是由调用方式决定的(是否用new调用),命名只是区分,是规范,告诉我们改用什么方式去调用,以免混淆。

2、Object构造函数
Object instanceof Object // true
Object instanceof Function // true
typeof Object // 'function'

Object既是对象,也是函数,我们称这种对象为函数对象,是js内置的构造函数。所有的函数对象都有一个默认属性为prototype(原型),这个属性Object.prototype是一个对象,所以称为原型对象,它的属性集合有:

    console.log(Object.prototype);// 输出如下:

    constructor:Object()
    __proto__: null
    hasOwnProperty

    __defineGetter__
    __defineSetter__
    __lookupGetter__
    __lookupSetter__
    isPrototypeOf
    propertyIsEnumerable
    toLocaleString
    toString
    valueOf
    get __proto__
    set __proto__

特别重要的三个属性:

1)、Object.prototype.constructor是指向当前内置函数对象的引用。如下:

Object.prototype.constructor === Object // true

2)、Object.prototype.__proto__存放的是继承关系,Object是一级函数对象,它没有继承与别的对象,所以它继承的是空对象null。如下:

Object.prototype.__proto__ === null // true

3)、hasOwnProperty判断对象的属性是否为原型中的属性。

二、内置二级构造函数Function(首字母大写)

Function instanceof Object // true
Function instanceof Function // true
typeof Function // 'function'

1、Function是内置构造函数,所以有一个默认属性为prototype,这个属性Function.prototype是一个对象,它的属性集合分别有:

constructor:Function()
__proto__: Object.prototype

// 继承Object的属性(方法) __defineGetter__ __defineSetter__ __lookupSetter__

Function.prototype.constructor是指向当前内置函数对象的引用,看:

Function.prototype.constructor === Function // true

js内置二级构造函数对象还有:

Date、Array、String、Boolean、RegExp、Number

我们可以自定义的二级函数对象,如:

 var Person = new Function();

    // 也可以这样(这只是一种快捷方式):

    function Person() {}

    Person instanceof Object // true
    Person instanceof Function // true
    typeof Person // 'function'

还是那句话:所以的对象都是由函数创建的。

重点:

1、二级构造函数的原型对象都是继承一级函数对象Object的原型对象,请看它们的原型指向:

Function.prototype.__proto__ === Object.prototype   // true
Date.prototype.__proto__ === Object.prototype   // true
Array.prototype.__proto__ === Object.prototype   // true
String.prototype.__proto__ === Object.prototype   // true
Boolean.prototype.__proto__ === Object.prototype   // true
RegExp.prototype.__proto__ === Object.prototype   // true
Number.prototype.__proto__ === Object.prototype   // true

Person.prototype.__proto__ === Object.prototype   // true

什么情况下产生继承关系呢?个人觉得用new运算符 + 构造函数才会产生继承关系,但是这些二级构造函数对象都是js内置的,他们有很多私有属性也是内置的。

2、二级内置构造函数继承了一级构造函数Object的prototype中的所有属性,同时二级构造函数也有自己的私有属性,是Object的原型中没有的,我们经常用到的比如:

a). Date的私有属性
    getDate、getDay、getFullYear等等
b). String的私有属性
    length、slice、concat、search等等
c). Array的私有属性
    length、join、slice、splice等等

三、非内置函数对象(非内置函数对象的没有内置的私有属性)

1、常用的非内置函数对象,这种对象也称为实例,这些对象都是new + 构造函数创建的,存在继承关系

构造函数(函数对象),原型,实例之间的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的引用,即constructor,而实例都包含一个指向它继承的原型对象的引用即__proto__

1)、var obj = {};(new Object())
2)、var arr = [];(new Array())
3)、var Num = new Number();
4)、var Str = new String();

2、非函数对象(实例)没有原型,也就是没有prototype属性,但是非函数对象(实例)也是由函数创建的。既然它是由函数创建的,我们肯定能找到创建它的函数,如下:

a). json对象

var obj = new Object();// 很明显可以看出有Object函数创建
obj.constructor === Object // true

b). 数组对象

var arr = new Array();
arr.constructor === Array // true

数组和对象还可以通过快捷方式创建,如:[]和{},实质上也是通过函数创建的,这种快捷方式叫做语法糖。请看:

var obj = {};
obj.constructor === Object // true

var arr = [];
arr.constructor === Array // true

总结:非函数对象都有constructor属性指向创建该对象的函数。进一步说明对象都是由函数创建的。

3、非函数对象的继承关系

1)、由2可以知道,非函数对象都是通过一级或者二级内置函数创建的,有contructor直接指向创建函数。那么他们的继承关系是怎样的呢,是否有__proto__直接指向它继承的对象?答案是当然有啦。在js中,只要你是一个对象,不是你继承别的对象就是别的对象继承你,我们叫这种通过原型对象继承属性的方式叫原型链。看:

a). json对象

var obj = {};
obj.__proto__ === Object.prototype // true
Object.prototype.__proto__ === null // true

// 这就是obj最长的原型链了
obj.__proto__.__proto__ === null // true

b). 数组对象

var arr = [];
arr.__proto__ === Array.prototype // true
Array.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__ === null // true

// 数组对象最长的原型链
arr.__proto__.__proto__.__proto__ === null // true

同理,其他非函数对象皆一样。

2)、看一下非函数对象是否真的继承了别的对象的属性(方法)

a). 数组对象

var str = [1, 2, 3, 'a', 'b', 'c'];
str.slice(2, 5); // [3, "a", "b"]

结果很明显,我们经常用就不多举例了。

4、非函数对象的内置对象arguments

arguments只出现在函数中,伪数组,格式和数组一样:[1, 2, 3],用于接收函数的参数。由Object创建,如下:

function arg() {

    console.log(arguments instanceof Object); // true
    console.log(arguments.constructor === Object); // true
    console.log(arguments.__proto__ === Object.prototype); // true

    console.log(typeof arguments.slice); // undefined
}

内置属性length,可以通过索引访问对应得值。它不是数组,不能用数组的方法。如:

arguments.slice(); // undefined

如何转化为数组,从而可以使用数组的所以方法?

 // Array.prototype.slice.call(arguments);

    function arg() {

    var arguments = Array.prototype.slice.call(arguments);

    console.log(arguments instanceof Object); // true
    console.log(arguments.constructor === Object); // false
    console.log(arguments.__proto__ === Object.prototype); // false

    console.log(typeof arguments.slice); // 'function'
}

四、一级构造函数对象Object原型中的属性hasOwnProperty

一级构造函数对象Object原型中的属性hasOwnProperty会被所有的函数对象或者非函数对象继承。

我们都知道对象是可以自定义属性,如何判断一个属性是自定义的还是内置的或者继承原型链中的呢?就看hasOwnProperty了本身了。

那么我们哪些地方会用到hasOwnProperty。

1、判断

var obj = {
        a: 'A',
        b: 'B'
};
obj.hasOwnProperty('toString'); // false(继承的)
obj.hasOwnProperty('a'); // true (对象本身的)

2、遍历

 Object.prototype.newProperty = 'newPropertyValue';
    var obj = {
        a: 'A',
        b: 'B'
    }
    for(var key in obj) {
        console.log(key + '==>' + obj[key])
    }

    // 结果:
    // a==>A
    // b==>B
    // newProperty==>newPropertyValue

    如何不遍历newProperty呢?

    for(var key in obj) {
        if(obj.hasOwnProperty(key)) {
            console.log(key + '==>' + obj[key])
        }
    }
  // 结果:
   // a==>A
   // b==>B
 

五、总结

  1、对象的定义:若干属性(方法)的集合;

  2、所有的函数都是对象,但是对象不一定是函数,既是对象又是函数的对象称为函数对象;

  3、所有的函数对象都有一个默认属性为prototype,这个属性prototype是一个对象。其中有一个属性为constructor,这个属性是当前函数的引用;另外一个是__proto__属性,指明继承关系。

  4、所有的非函数对象(实例)都有一个constructor属性,这个属性是一个存放着创建该对象的函数;同时也有__proto__属性,指明继承关系。

  5、由第3、4点可以说明:所有的对象(函数对象和非函数对象)都是由函数创建的。

  6、什么是原型对象?

  每一个函数对象都有的一个prototype属性,这个属性是一个对象,叫原型对象。只有函数(对象)才有原型,才能被继承。

  7、什么是原型链(也叫原型对象链)?

  用来继承属性(方法)的一条(原型)对象链。


  (js任何一个对象不是被继承就是继承别的对象,所以js对象总能找到一条属于它的用来继承属性(方法)的(原型)对象链。)

很多地方都参考了别的文章,但是都忘了出处,就不列举了......,有错欢迎指出!

原文地址:https://www.cnblogs.com/yanmuxiao/p/8561914.html