JS函数执行

一个JavaScript函数fn,被执行有三种途径:

  • fn()
  • new fn()
  • fn.call()或fn.apply()

new机制以及继承

JavaScript中定义了一种对象,称之为ECMAScript对象,其内部实现包括以下:

  • __call__: 说明该对象可以被执行,具有function属性
  • __construct__: 说明该对象可以接受new操作,具有构造器属性
  • __prototype__: 指向对象的原型链。对于定义的函数,会指向Function.prototype

注意:__prototype__是原型链,对所有对象都有的。prototype是原型,是函数才有的,就是一个普通的对象而已,目的是为了接受new后给生成的对象提供原型链的。

执行fn就是调用__call__

执行new fn()会进行以下简化过程:

  • 新建一个对象,记作o
  • 把o.__prototype__指向fn.prototype(如果fn.prototype不是一个Object,则指向Object.prototype)
  • 执行fn,并用o作为this(即内部实现的fn.call(this))。如果fn返回是一个object,则返回object, 否则把o返回

fn.call(obj)或者fn.apply(obj)就是将obj作为this,执行fn。本质是调用__call__,只是传入了obj作为this.

    //定义一个函数,正常函数会具有__call__, __construct__//同时Parent.__proto__指向Function.prototypefunction Parent() {this.sayAge = function() {console.log("age is: " + this.age);}}//原型上添加一个方法Parent.prototype.sayParent = function(){console.log("this is Parent Method");}//定义另一个函数function Child(firstname){//这里就是调用Parent的__call__, 并且传入this//而这里的this,是Child接受new时候生成的对象//因此,这一步会给生成的Child生成的实例添加一个sayAge属性Parent.call(this);this.fname = firstname;this.age = 40;this.saySomething = function() {console.log(this.fname);this.sayAge();}}//这一步就是new的调用,按原理分步来看//1. 新建了个对象,记作o//2. o.__prototype__ = Parent.prototype, 因此o.sayParent会访问到o.__prototype__.sayParent(原型链查找机制)//3. Parent.call(o), 因此o也会有个sayAge属性(o.sayAge)//4. Child.prototype = o, 因此 Child.prototype 通过o.__prototype__ 这个原型链具有了o.sayParent属性,同时通过o.sayAge 具有了sayAge属性(也就是说Child.prototype上具有sayAge属性,但没有sayParent属性,但是通过原型链,也可以访问到sayParent属性)Child.prototype = new Parent();//这也是一步new调用//1. 新建对象,记作s//2. s.__prototype__ = Child.prototype, 此时s会具有sayAge属性以及sayParent这个原型链上的属性//3. Child.call(s), 执行后, 增加了fname, age, saySomething属性, 同时由于跑了Parent.call(s), s还具有sayAge属性, 这个属性是s身上的, 上面那个sayAge是Child.prototype上的, 即s.__prototype__上的。//4. child = svar child = new Child("张")//child本身属性就有,执行child.saySomething();//child本身属性没有, 去原型链上看, child.__prototype__ = s.__prototype__ = Child.prototype = o, 这里没找到sayParent, 继续往上找, o.__prototype__ = Parent.prototype, 这里找到了, 执行(第二层原型链找到)child.sayParent();

原理来看写得有些繁琐,本身其实是比较简单的东西。
重点是new的过程,原型prototype和原型链__prototype__
也正是new的原理,导致了原型链的继承,本质是生成的对象的__prototype__指向了函数的原型prototype

更复杂的调用继承之类的,都可以通过这个原理来理解。说白了,原型链继承就是复用了prototype而已。

本例来看,Child中的Parent.call(this)看似没有必要,但本质上是有区别的。如果去掉这一句,则Child的实例本身将没有sayAge属性,而Child.prototype具有sayAge属性,因此实例的__prototype__具有sayAge属性,因此还可以执行。

但目的是为了继承,因此属性是需要对象上本身持有,而非是通过原型链上来访问,所以加上这一句是原理上的严谨要求。可以通过下面的例子来检验:

    function Parent() {this.sayAge = function() {console.log("age is: " + this.age);}}Parent.prototype.sayParent = function(){console.log("this is Parent Method");}function Child(firstname){Parent.call(this); this.fname = firstname;this.age = 40;this.saySomething = function() {console.log(this.fname);this.sayAge();}}Child.prototype = new Parent();var child = new Child("张")child.saySomething();child.sayParent();console.log(child.hasOwnProperty('sayAge')); // truechild.sayAge(); //能调用,此时调用的是自身的属性delete child.sayAge; // delete只能删除自身的属性,不能删除原型链属性console.log(child.hasOwnProperty('sayAge')); // false,自身没有这个属性了child.sayAge(); //还能调用,此时调用的是原型链上的方法

如果删掉Parent.call(this), 上面两句child.hasOwnProperty('sayAge'), 都将返回false

Javascript函数执行、new机制以及继承相关推荐

  1. [译]深入理解JavaScript函数执行—调用栈,事件循环和任务等

    Web 开发者,或者前端工程师(我们更喜欢别人这么称呼)现如今几乎能做所有的工作,从扮演一个浏览器内部交互性的角色,到制作电脑游戏.桌面控件.跨平台手机应用,甚至还可以把它写在服务器端(最流行的是no ...

  2. 【译】理解Javascript函数执行—调用栈、事件循环、任务等

    原文作者:Gaurav Pandvia 原文链接:medium.com/@gaurav.pan- 文中部分链接可能需要梯子. 欢迎批评指正. 现如今,web开发者(我们更喜欢被叫做前端工程师)用一门脚 ...

  3. 「译」理解Javascript函数执行—调用栈、事件循环、任务等

    现如今,web开发者(我们更喜欢被叫做前端工程师)用一门脚本语言就能做任何事情,从提供浏览器中的交互,到开发电脑游戏.桌面工具.跨平台移动应用,甚至可以在服务端部署(如最流行的Node.js)来连结任 ...

  4. javascript函数执行前期变量环境初始化过程

    示例代码如下: var d = '全局的'; (function(){  alert(d == undefined);//结合步骤4  var d = 123; })(); (function(a){ ...

  5. js执行oracle函数吗,执行javascript函数

    JS--函数 JavaScript 函数语法 函数就是包裹在花括号中的代码块,前面使用了关键词 function: function 函数名([参数]){ 执行的代码: } 当调用该函数时,会执行函数 ...

  6. 理解JavaScript的执行机制

    一直没有深入了解过JavaScript的事件执行机制,直到看到了这篇文章:<这一次,彻底弄懂JavaScript执行机制> 才发觉熟悉JavaScript的执行机制非常重要. 毕竟在跟进项 ...

  7. javascript的执行机制

    目录 浏览器的组成 预编译 事件循环 JS的事件循环(eventloop)是怎么运作的? 尾调用 尾递归 尾递归(尾调用)优化 浏览器的组成 1.User Interface(用户界面)-包括地址栏. ...

  8. JavaScript 函数声明,函数表达式,匿名函数,立即执行函数之区别

    函数声明:function fnName () {-};使用function关键字声明一个函数,再指定一个函数名,叫函数声明. 函数表达式 var fnName = function () {-};使 ...

  9. 了解javascript中函数执行顺序

    我个人觉得一般人不会有这种写法,但艺不压身,呵呵.希望能帮到初学的朋友!大家一起进步! 首先列举出8个例子,然后例子的解答会在文章末尾贴出! 测试代码一: <script language=&q ...

最新文章

  1. python global函数_如何使用python语言中的global关键字获取函数值
  2. Java并发编程 synchronized保证线程安全的原理
  3. NRF52810能不能替代NRF52832
  4. Ubuntu/linux 系统彻底杀死一个进程的方法
  5. DevOps自动化工具集合
  6. java修改title_js修改title
  7. 【RK3399Pro学习笔记】一、Thinker Edge R 安装系统和VNC服务
  8. React开发(109):引用错误 报错
  9. android 4.4 linux 内核版本,稳定内核版本和更新  |  Android 开源项目  |  Android Open Source Project...
  10. SpringBoot配置Profile以及配置文件的加载位置
  11. python // 运算符
  12. 小米蓝牙键盘怎么连接_不是吐槽,是推荐!买了个小米旗下的蓝牙双模键盘。。。...
  13. 华为设备配置SEP多实例
  14. vscode更改配置文件路径_Visual Studio Code安装和配置
  15. HBuilder X 无法启动微信开发者工具问题解决方法
  16. GateWay 服务网关
  17. 主机识别SDIO接口卡过程
  18. xmind可以画流程图吗_Xmind制作流程图教程 xmind怎么画流程图?xmind画流程图步骤...
  19. 在SpringBoot中启用Http2.0
  20. 读书感受 之 《数学之美》

热门文章

  1. Javascript鼠标滚轮事件兼容写法
  2. String字符串位置移动
  3. 如何在数据库中高效实现订座功能?
  4. 对象存储与块存储、文件存储等对比
  5. 无法启动MySQL数据库
  6. mosquitto---config.mk
  7. x264的一些参数设置对编码效率的影响
  8. 从零开始学ios开发(十二):Table Views(中)UITableViewCell定制
  9. 建立普通用户信任关系,
  10. linux服务之DHCP