this

this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window; 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用。 我们还可以使用apply和call两个全局方法来改变函数中this的具体指向。

先看一个在全局作用范围内使用this的例子:

        <script type="text/javascript">console.log(this === window);  // trueconsole.log(window.alert === this.alert);  // trueconsole.log(this.parseInt("021", 10));  // 10</script>        

函数中的this是在运行时决定的,而不是函数定义时,如下:

        // 定义一个全局函数function foo() {console.log(this.fruit);}// 定义一个全局变量,等价于window.fruit = "apple";var fruit = "apple";// 此时函数foo中this指向window对象// 这种调用方式和window.foo();是完全等价的foo();  // "apple"// 自定义一个对象,并将此对象的属性foo指向全局函数foovar pack = {fruit: "orange",foo: foo};// 此时函数foo中this指向window.pack对象pack.foo(); // "orange"        

全局函数apply和call可以用来改变函数中this的指向,如下:

        // 定义一个全局函数function foo() {console.log(this.fruit);}// 定义一个全局变量var fruit = "apple";// 自定义一个对象var pack = {fruit: "orange"};// 等价于window.foo();foo.apply(window);  // "apple"// 此时foo中的this === packfoo.apply(pack);    // "orange"

注:apply和call两个函数的作用相同,唯一的区别是两个函数的参数定义不同。

因为在JavaScript中函数也是对象,所以我们可以看到如下有趣的例子:

        // 定义一个全局函数function foo() {if (this === window) {console.log("this is window.");}}// 函数foo也是对象,所以可以定义foo的属性boo为一个函数foo.boo = function() {if (this === foo) {console.log("this is foo.");} else if (this === window) {console.log("this is window.");}};// 等价于window.foo();foo();  // this is window.// 可以看到函数中this的指向调用函数的对象foo.boo();  // this is foo.// 使用apply改变函数中this的指向foo.boo.apply(window);  // this is window.

prototype

我们已经在第一章中使用prototype模拟类和继承的实现。 prototype本质上还是一个JavaScript对象。 并且每个函数都有一个默认的prototype属性。 
如果这个函数被用在创建自定义对象的场景中,我们称这个函数为构造函数。 比如下面一个简单的场景:

        // 构造函数function Person(name) {this.name = name;}// 定义Person的原型,原型中的属性可以被自定义对象引用Person.prototype = {getName: function() {return this.name;}}var zhang = new Person("ZhangSan");console.log(zhang.getName());   // "ZhangSan"
作为类比,我们考虑下JavaScript中的数据类型 - 字符串(String)、数字(Number)、数组(Array)、对象(Object)、日期(Date)等。 我们有理由相信,在JavaScript内部这些类型都是作为构造函数来实现的,比如:
        // 定义数组的构造函数,作为JavaScript的一种预定义类型function Array() {// ...}// 初始化数组的实例var arr1 = new Array(1, 56, 34, 12);// 但是,我们更倾向于如下的语法定义:var arr2 = [1, 56, 34, 12];
同时对数组操作的很多方法(比如concat、join、push)应该也是在prototype属性中定义的。 实际上,JavaScript所有的固有数据类型都具有只读的prototype属性(这是可以理解的:因为如果修改了这些类型的prototype属性,则哪些预定义的方法就消失了), 但是我们可以向其中添加自己的扩展方法。
        // 向JavaScript固有类型Array扩展一个获取最小值的方法Array.prototype.min = function() {var min = this[0];for (var i = 1; i < this.length; i++) {if (this[i] < min) {min = this[i];}}return min;};// 在任意Array的实例上调用min方法console.log([1, 56, 34, 12].min());  // 1
注意:这里有一个陷阱,向Array的原型中添加扩展方法后,当使用for-in循环数组时,这个扩展方法也会被循环出来。 下面的代码说明这一点(假设已经向Array的原型中扩展了min方法):
        var arr = [1, 56, 34, 12];var total = 0;for (var i in arr) {total += parseInt(arr[i], 10);}console.log(total);   // NaN
解决方法也很简单:
        var arr = [1, 56, 34, 12];var total = 0;for (var i in arr) {if (arr.hasOwnProperty(i)) {total += parseInt(arr[i], 10);}}console.log(total);   // 103

constructor

constructor始终指向创建当前对象的构造函数。比如下面例子:

        // 等价于 var foo = new Array(1, 56, 34, 12);var arr = [1, 56, 34, 12];console.log(arr.constructor === Array); // true// 等价于 var foo = new Function();var Foo = function() { };console.log(Foo.constructor === Function); // true// 由构造函数实例化一个obj对象var obj = new Foo();console.log(obj.constructor === Foo); // true// 将上面两段代码合起来,就得到下面的结论console.log(obj.constructor.constructor === Function); // true

但是当constructor遇到prototype时,有趣的事情就发生了。 
我们知道每个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数。如下例所示:

        function Person(name) {this.name = name;};Person.prototype.getName = function() {return this.name;};var p = new Person("ZhangSan");console.log(p.constructor === Person);  // trueconsole.log(Person.prototype.constructor === Person); // true// 将上两行代码合并就得到如下结果console.log(p.constructor.prototype.constructor === Person); // true
当时当我们重新定义函数的prototype时(注意:和上例的区别,这里不是修改而是覆盖), constructor的行为就有点奇怪了,如下示例:
        function Person(name) {this.name = name;};Person.prototype = {getName: function() {return this.name;}};var p = new Person("ZhangSan");console.log(p.constructor === Person);  // falseconsole.log(Person.prototype.constructor === Person); // falseconsole.log(p.constructor.prototype.constructor === Person); // false
为什么呢? 原来是因为覆盖Person.prototype时,等价于进行如下代码操作:
        Person.prototype = new Object({getName: function() {return this.name;}});
而constructor始终指向创建自身的构造函数,所以此时Person.prototype.constructor === Object,即是:
        function Person(name) {this.name = name;};Person.prototype = {getName: function() {return this.name;}};var p = new Person("ZhangSan");console.log(p.constructor === Object);  // trueconsole.log(Person.prototype.constructor === Object); // trueconsole.log(p.constructor.prototype.constructor === Object); // true
怎么修正这种问题呢?方法也很简单,重新覆盖Person.prototype.constructor即可:
        function Person(name) {this.name = name;};Person.prototype = new Object({getName: function() {return this.name;}});Person.prototype.constructor = Person;var p = new Person("ZhangSan");console.log(p.constructor === Person);  // trueconsole.log(Person.prototype.constructor === Person); // trueconsole.log(p.constructor.prototype.constructor === Person); // true

转载于:https://www.cnblogs.com/super86/p/3962882.html

JavaScript——this、constructor、prototype相关推荐

  1. @PostConstruct、@PreDestroy注解介绍及Spring中@PostConstruct、constructor、@Autowired的顺序

    @PostConstruct和@PreDestroy @PostConstruct和@PreDestroy注解是Common Annotations中的注解,Common Annotations原本是 ...

  2. JS中关于构造函数、原型链、prototype、constructor、instanceof、__proto__属性

    在Javascript不存在类(Class)的概念,javascript中不是基于类的,而是通过构造函数(constructor)和原型链(prototype chains)实现的.但是在ES6中引入 ...

  3. Java 反射 (Class、ClassLoader、Constructor、Method、Field)

    反射是Java中一个非常重要.非常强大的机制.曾看到一句话"反射是框架的灵魂",初学时不懂,等到学完框架之后才慢慢理解其意. 什么是反射?我们先通过几个类和示例来初步体会一下反射. ...

  4. 傲娇大少之---【JS的原型,prototype、__proto__、constructor】

    不求甚解 - - liao一下prototype 如果你爱我,就干了这碗热热的毒鸡汤! 在父母的期望面前我们不敢说不行,我们总是用行动告诉他们我们是真的不行.欧耶! 关于prototype,怎么说呢, ...

  5. 基于SpringBoot的后台管理系统(异常、注解、node、page)(二)

    common.exception.annotation.node.page 说明 如果您有幸能看到,请认阅读以下内容: 1.本项目临摹自abel533的Guns,他的项目 fork 自 stylefe ...

  6. 以太坊学习路线——(三)Solidity常用IDE搭建、合约编译、部署、调用

    这篇博客演示的基本操作系统环境是CentOS 7,参考书籍:以太坊开发实战--以太坊关键技术与案例分析 第七.九章(吴寿鹤.冯翔.刘涛.周广益   著). 一.常用IDE 1.remix:是一个浏览器 ...

  7. Java 编程问题:七、Java 反射类、接口、构造器、方法和字段

    原文:Java Coding Problems 协议:CC BY-NC-SA 4.0 贡献者:飞龙 本文来自[ApacheCN Java 译文集],自豪地采用谷歌翻译. 本章包括涉及 Java 反射 ...

  8. Lombok简介、使用、工作原理、优缺点(转载)

    文章转载:https://www.jianshu.com/p/453c379c94bd Lombok简介.使用.工作原理.优缺点 1.Lombok简介 官方介绍 Project Lombok is a ...

  9. Javascript - prototype、__proto__、constructor

    最近看了很多文章,想要更通透的搞懂JS中的prototype.__proto__与constructor属性,从各个博主的文章里摘取了我认为可以有助于理解的一些内容,希望自己能够掌握好这一重要知识点的 ...

最新文章

  1. matlab 实例均命名为,MATLAB复习题
  2. Codeforces Round #658 (Div. 2)部分题解
  3. Python源码剖析学习二
  4. 经典C语言程序100例之一
  5. App主流UI框架结构
  6. CSS3幻灯片制作心得
  7. 《C prime plus (第五版)》 ---第11章 字符串和字符串函数---4
  8. aggregation java_Elasticsearch Aggregation 多个字段分组统计 Java API实现
  9. 关于键盘事件中keyCode、which和charCode 的兼容性测试
  10. oracle分析函数sum() over()
  11. LabVIEW网络数据传输远程控制编程与验证测试
  12. 计算机应用技术实验教程 网页制作,HtmlExperiment网页制作基础教程
  13. kindle3使用技巧
  14. 正则表达式在工作中的应用
  15. 解决MAC上网速度慢的原因
  16. Linux之更改配置文件永久修改IP地址
  17. JUNIPER路由器配置
  18. 中国移动网上商城有多坑?
  19. IDEA加载jar包步骤
  20. 3D游戏建模如火如荼,你还在为未来迷茫吗?

热门文章

  1. 如何在Unity中播放影片
  2. struts2 action之间参数的传递
  3. 分析一个文本(英文文章)(300k—500k)中的词出现的频率,并且把频率最高的10个词打印出来。...
  4. [置顶] 我整理的一些常用网址
  5. Enterprise Library: Data Access Application Block配置文件分析篇
  6. 剑灵总显示服务器断开连接,求解一分钟“与服务器断开连接”问题
  7. css选择器权重排序_CSS选择器的权重与优先规则
  8. hbase数据读取优化_从hbase读取数据优化策略和实验对照结果
  9. auth0的java-jwt_Spring boot + JWT 实现安全验证 ---auth0.jwt
  10. 服务器与ssl证书有关吗,选择网站SSL证书和服务器位置有没有关系和影响