在Javascript不存在类(Class)的概念,javascript中不是基于类的,而是通过构造函数(constructor)和原型链(prototype chains)实现的。但是在ES6中引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让原型对象的写法更加清晰、更像面向对象编程的语法而已。

1.构造函数的简单介绍

  构造函数就是提供了一个生成对象的模板并描述对象的基本结构的函数。一个构造函数,可以生成多个对象,每个对象都有相同的结构。总的来说,构造函数就是对象的模板,对象就是构造函数的实例。

  构造函数的特点有:

    a:构造函数的函数名首字母必须大写。

    b:内部使用this对象,来指向将要生成的对象实例。

    c:使用new操作符来调用构造函数,并返回对象实例。

function Person(){this.name = 'keith';
}
var boy = new Person();
console.log(boy.name); //'keith'

2.构造函数的缺点

所有的对象实例都可以继承构造函数中的属性和方法。但是,同一个对象实例之间,无法共享属性。

function Person(name,height){this.name=name;this.height=height;this.hobby=function(){return 'watching movies';}
}
var boy=new Person('keith',180);
var girl=new Person('rascal',153);
console.log(boy.name); //'keith'
console.log(girl.name); //'rascal'
console.log(boy.hobby===girl.hobby); //false

上面代码中,一个构造函数Person生成了两个对象实例boy和girl,并且有两个属性和一个方法。但是,它们的hobby方法是不一样的。也就是说,每次使用new来调用构造函数生成一个对象实例的时候,都会创建一个hobby方法。这既没有必要,又浪费资源,因为所有hobby方法都是同样的行为,完全可以被两个对象实例共享。

所以,构造函数的缺点就是:同一个构造函数的对象实例之间无法共享属性或方法。

3.prototype属性

为了解决构造函数的对象实例之间无法共享属性的缺点,JS 提供了prototype属性。

函数有一个特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。通过prototype定义的属性及方法能被所有对象实例所共享,这就是prototype的意义。

通过构造函数生成对象实例时,会将对象实例的原型指向构造函数的prototype属性。每一个构造函数都有一个prototype属性,这个属性就是对象实例的原型对象。

function Person(name,height){this.name=name;this.height=height;
}
Person.prototype.hobby=function(){     //这样声明构造函数的方法可以实现对象实例共享方法return 'watching movies';
}
var boy=new Person('keith',180);
var girl=new Person('rascal',153);
console.log(boy.name); //'keith'
console.log(girl.name); //'rascal'
console.log(boy.hobby===girl.hobby); //true

上面代码中,如果将hobby方法放在原型对象上,那么两个实例对象都共享着同一个方法。对于构造函数来说,prototype是作为构造函数的属性;对于对象实例来说,prototype是对象实例的原型对象。所以prototype即是属性,又是对象。

原型对象的属性不是对象实例的属性。对象实例的属性是继承自构造函数定义的属性,因为构造函数内部有一个this关键字来指向将要生成的对象实例。对象实例的属性,其实就是构造函数内部定义的属性。只要修改原型对象上的属性和方法,变动就会立刻体现在所有对象实例上。

Person.prototype.hobby=function(){return 'swimming';
}
console.log(boy.hobby===girl.hobby); //true
console.log(boy.hobby()); //'swimming'
console.log(girl.hobby()); //'swimming'

上面代码中,当修改了原型对象的hobby方法之后,两个对象实例都发生了变化。这是因为对象实例其实是没有hobby方法,都是读取原型对象的hobby方法。也就是说,当某个对象实例没有该属性和方法时,就会到原型对象上去查找。如果实例对象自身有某个属性或方法,就不会去原型对象上查找。

boy.hobby=function(){return 'play basketball';
}
console.log(boy.hobby()); //'play basketball'
console.log(girl.hobby()); //'swimming'

上面代码中,boy对象实例的hobby方法修改时,就不会在继承原型对象上的hobby方法了。不过girl仍然会继承原型对象的方法。

  总结如下:

  a:原型对象的作用,就是定义所有对象实例所共享的属性和方法。

  b:prototype,对于构造函数来说,它是一个属性;对于对象实例来说,它是一个原型对象。

4.原型链(prototype chains)

对象的属性和方法,有可能是定义在自身,也有可能是定义在它的原型对象。由于原型对象本身也是对象,它也有自己的原型,所以形成了一条原型链(prototype chain)。比如,b对象是a对象的原型,c对象是b对象的原型,以此类推。所有一切的对象的原型顶端,都是Object.prototype,即Object构造函数的prototype属性指向的那个对象。

当然,Object.prototype对象也有自己的原型对象,那就是没有任何属性和方法的null对象,而null对象没有自己的原型。

console.log(Object.getPrototypeOf(Object.prototype)); //null
console.log(Person.prototype.isPrototypeOf(boy)) //true

原型链(prototype chain)的特点有:

  a:读取对象的某个属性时,JavaScript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。

  b:如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overiding)。

  c:一级级向上在原型链寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。

var arr=[1,2,3];
console.log(arr.length); //3
console.log(arr.valueOf()) //[1,2,3]
console.log(arr.join('|')) //1|2|3

上面代码中,定了一个数组arr,数组里面有三个元素。我们并没有给数组添加任何属性和方法,可是却在调用length,join(),valueOf()时,却不会报错。

length属性是继承自Array.prototype的,属于原型对象上的一个属性。join方法也是继承自Array.prototype的,属于原型对象上的一个方法。这两个方法是所有数组所共享的。当实例对象上没有这个length属性时,就会去原型对象查找。valueOf方法是继承自Object.prototype的。首先,arr数组是没有valueOf方法的,所以就到原型对象Array.prototype查找。然后,发现Array.prototype对象上没有valueOf方法。最后,再到它的原型对象Object.prototype查找。

来看看Array.prototype对象和Object.prototype对象分别有什么属性和方法。

console.log(Object.getOwnPropertyNames(Array.prototype))
//["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push", "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf", "forEach", "map", "filter", "reduce", "reduceRight", "some", "every", "find", "findIndex", "copyWithin", "fill", "entries", "keys", "values", "includes", "constructor", "$set", "$remove"]
 console.log(Object.getOwnPropertyNames(Object.prototype))// ["toSource", "toString", "toLocaleString", "valueOf", "watch", "unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__", "constructor"]

5.constructor属性

对象实例的constructor属性返回对象实例的构造函数。

function A(){};
var a = new A();
console.log(a.constructor); //A

constructor 属性实际上是原型对象的属性,这个属性包含了一个指针,指回原构造函数,它被所有实例对象继承。

function A(){};
var a = new A();
console.log(A.prototype.constructor === A) //true  通过原型对象访问constructor属性返回的是原型对象所处的构造函数
console.log(a.constructor === A.prototype.constructor);//true

console.log(A.hasOwnProperty('prototype')); //true
console.log(A.prototype.hasOwnProperty('constructor')); //true

5.1:constructor属性的作用

  a:判断对象实例的构造函数

function A(){};
var a = new A();
console.log(a.constructor === A)    //true
console.log(a.constructor === Array) //false

  b:从实例新建另一个实例

function A() {};
var a = new A();
var b = new a.constructor();  //从a.constructor间接调用构造函数。
console.log(b instanceof A); //true

6.instanceof运算符

instanceof 判断对象是否为某个构造函数的实例。

function A() {};
var a = new A();
console.log(a instanceof A); //true
console.log(a instanceof Object); //true   对整个原型链上的对象都有效

注意,instanceof对象只能用于复杂数据类型(数组,对象等),不能用于简单数据类型(布尔值,数字,字符串等)。而且 null 和 undefined 都不是对象,所以instanceof 总是返回false。

console.log([1] instanceof Array); //true
console.log({} instanceof Object); //true
console.log(true instanceof Boolean); //false
console.log('aaa' instanceof String); //false

console.log(null instanceof Object); //false
console.log(undefined instanceof Object); //false

利用instanceof运算符,还可以巧妙地解决,调用构造函数时,忘了加new命令的问题。

function Keith(name,height) {if (! this instanceof Keith) {return new Keith(name,height);
}
this.name = name;
this.height = height;
}

7、__proto__属性(注意两边都是两个_符号)

属性 __proto__ 返回对象实例的原型对象,通过该属性可以访问原型对象的所有方法。

var Person = function(name){this.name = name;
}
var a = new Person('jack');
console.log(a.__proto__  === Person.prototype);    //true  同理,Person也有原型对象,通过Person的 __proto__ 属性也可以访问到它的原型对象,以此类推,可以实现原型链的向上追溯。

可以通过 __proto__ 属性继承其他对象的属性,但非常不建议这么做,对性能影响非常大。详情查看:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

var Person = function (name) {this.name = name;
}
var a = new Person('jack');
console.log(a.name);     //jack
var b = {age: 10
}
a.__proto__ = b;
console.log(a.name,a.age);   //jack 10

相对于通过 __proto__ 属性继承其他对象的属性而言,Object.create() 方法是一个更加值得推荐的方法。该方法接收一个对象作为参数,返回一个以该对象为原型对象的新对象,即继承了作为参数的对象的属性及方法。

let b = {age: 10
}
let a = Object.create(b);
console.log(a.age);     //10

8、Function.prototype和Object.prototype

Function 和 Object 其实就是一个构造函数。

function Person(a,b){this.a = a;this.b = b;
}
let man = new Person('a','b');
console.log(Person.constructor);    //ƒ Function() { [native code] }
console.log(Person.constructor.constructor);    //ƒ Function() { [native code] }
console.log(Function == Person.constructor);    //true
console.log(Function.prototype == Person.__proto__);     //true

let obj = {}
console.log(obj.constructor);    //ƒ Object() { [native code] }
console.log(Object == obj.constructor);    //true
console.log(Object.prototype == obj.__proto__);    //true

面试题:是否所有的对象都继承 Object

不是,js中不是所有对象都继承Object,也有特例,null 和 undefined 不是继承自 Object。

转载于:https://www.cnblogs.com/wenxuehai/p/10178865.html

JS中关于构造函数、原型链、prototype、constructor、instanceof、__proto__属性相关推荐

  1. 构造函数 + 原型链继承 + 临摹面向对象模式的canvas动画框架

    感谢谢帅shawn分享的canvas动画框架,我再来分一次 //动画框架 http://neekey.net/blog/2011/05/11/canvas-%E7%AE%80%E5%8D%95%E5% ...

  2. js-4 代理Proxy,object原型链, prototype, 继承,

    1代理Proxy 1.什么是代理Proxy拦截? 可以对对象,函数,数组进行拦截,将其原本的函数操作改写. Proxy在目标对象前设一个拦截层,外界对该对象的访问都必须先通过这层拦截,因此提供了一种机 ...

  3. JS 面向对象编程、原型链、原型继承(个人学习总结)

    一.面向对象 1. 面向对象 是所有语言 都有的一种编程思想,组织代码的一种形式 基于对象的语言:JS语言 面向对象的语言:c++ java c# 2. 面向对象 3大特征 封装:将重用代码封装到函数 ...

  4. JS数据类型 构造函数 原型链

    js数据类型 基本数据类型:string   undefined   null  boolean  number 引用数据类型  Object  array  function 二者的区别 基本数据类 ...

  5. js构造函数(原型链)及Es6的class类

    js函数 首先弄明白何为函数呢,函数简单的说就是重复执行的代码块.函数是这样的一段JavaScript 代码,它只定义一次,但可能被执行或调用任意次. 函数的定义方式: 1.声明式函数定义: func ...

  6. js的继承和原型链(更新中)

    话不多说,直接上MDN链接 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_ ...

  7. [js高手之路]原型对象(prototype)与原型链相关属性与方法详解

    一,instanceof: instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型. 我在之前的两篇文章 [js高手之路]构造函数的基本特性与优缺点 [js高手 ...

  8. 原生JS 对象 包装类 原形 原型链

    对象 用已学的知识点,描述一下你心目中的对象 var mrDeng = { name : "mrDeng",age : 40,sex : "male",heal ...

  9. 一文读懂原型链 prototype和__proto__详解

    目录 1.原型对象 prototype 2.prototype 和 __proto__ 3.原型链 4.补充 5.原型链总结 1.原型对象 prototype 我们首先总结一下原型对象的作用: 原型对 ...

最新文章

  1. C# 获取MAC地址
  2. MySQL主从复制故障案例一
  3. python 函数前有一个下划线_【Python】怎么写好一个 Python 函数?
  4. Mock Server实践
  5. 协程 eventlet
  6. 【转】TIOBE 12 月排行榜:古老的 C 和后起之秀 Kotlin,谁是年度编程语言之王?...
  7. 在HDFS集群中优化secondary namenode到datanode1节点上,并做重启hdfs集群后,datanode1启动失败...
  8. vue项目pc端页面适配
  9. 如何将较大的PDF文件压缩变小?PDF压缩方法!
  10. 中职计算机教师发言范文话,中职班主任发言稿范文(精选4篇)
  11. JS中alert的三种使用方式
  12. C语言中的scanf、getchar、putchar、gets、puts
  13. elementUI 时间格式化
  14. 2022年全球与中国环己胺市场现状及未来发展趋势
  15. windows安装maven
  16. 自行车LED灯导航仪推出 配自行车专用地图
  17. flac格式如何转mp3,3招帮你搞定
  18. 泛微oa数据库之查询流程批次条件、出口条件
  19. 优思学院:质量管理七大手法,就是六西格玛的起点
  20. java基于springboot+vue的旅游博客旅游经验分享系统

热门文章

  1. Elasticsearch的前后台运行与停止(tar包方式)
  2. AngularJs ng-repeat限制循环次数
  3. Asp.NetWebForm的控件属性
  4. 关于angularjs input上传图片前获取图片的Size 浅析
  5. 详解EBS接口开发之采购申请导入
  6. 利用 Linux tap/tun 虚拟设备写一个 ICMP echo 程序
  7. Command 传参的几种方式
  8. windows下实现自己的第一个python脚本文件并.exe运行
  9. 南海发展大数据产业 建设新型智慧城市
  10. 低学历者为何能骗取30万年薪职位