JavaScript函数、闭包、原型、面向对象

断言

单元测试框架的核心是断言方法,通常叫assert()。

该方法通常接收一个值--需要断言的值,以及一个表示该断言目的的描述。

如果该值执行的结果为true,断言就会通过;

否则,断言就会被认为是失败的。

通常用一个相应的通过(pass)/ 失败(fail)标记记录相关的信息;

function assert(value, desc) {
let li = document.createElement('li');
li.className = value ? 'pass' : 'fail';
li.appendChild(document.createTextNode(desc));
document.getElementById('results').appendChild(li);
} // 断言函数
function assert(value, desc) {
if (value) {
console.log(`\033[32m ${desc} \033[0m`); // 断言通过 绿色字体
} else {
console.log(`\033[31m ${desc} \033[0m`); // 断言失败 红色字体
}
}

函数

  • JavaScript是一门函数式语言

  • 在JavaScript中,函数是第一型对象。函数可以共处,可以视作为其他任意类型的对象。就像普通的JavaScript数据类型,,函数可以被任意变量进行引用,或声明成对象字面量,甚至可以将其作为函数参数进行传递。

  • 函数是第一型对象

    • 可以通过字面量进行创建。
    • 可以赋值给变量、数组或其他对象的属性。
    • 可以作为参数传递给函数。
    • 可以作为函数的返回值进行返回。
    • 可以拥有动态创建并赋值的属性。
  • 命名一个函数时,该名称在整个函数声明范围内是有效的。如果函数声明在顶层,window对象上的同名属性则会引用到该函数。

  • 所有的函数都有一个name属性,该属性保存的是该函数名称的字符串。匿名函数的name属性值为空。

  • 在JavaScript中,作用域是由function进行声明的,而不是代码块。声明的作用域创建于代码块,但不是终结于代码块(其他语言是终结于代码块的)

if (window) {
var x = 123;
}
alert(x); 执行代码后,会弹出123,是因为JavaScript在大括号关闭处并没有终止其作用域。
  • 变量声明的作用域开始于声明的地方,结束于函数的结尾,与代码嵌套无关。

  • 命名函数的作用域是指声明该函数的整个函数范围,与代码嵌套无关;

  • 对于作用域声明,全局上下文就像一个包含页面所有代码的超大型函数。

  • 所有的函数调用都会传递两个隐式参数:argument和this

作为函数进行调用

如果一个数不是作为方法、构造器、或者通过apply()或call()进行调用的,则认为它是“作为函数”进行调用的。

function ninja() {};
ninja() var samurai = function() {};
samurai()
  • 以这种方式调用时,函数的上下文是全局上下文---window对象。

作为方法进行调用

当一个函数被赋值给对象的一个属性,并使用引用该函数的这个属性进行调用时,那么函数就是作为该对象的一个方法进行调用的。

var 0 = {};
o.whatever = function() {};
o.whatever();
  • 将函数作为对象的一个方法进行调用时,该对象就变成了函数上下文,并且在函数内部可以以this参数的形式进行访问。

作为构造器进行调用

  • 将函数作为构造器进行调用,需要在函数调用前使用new关键字

    • 创建一个新的空对象;
    • 传递给构造器都对象是this参数,从而成为构造器的函数上下文;
    • 如果没有显式都返回值,新创建的对象则作为构造器的返回值进行返回。
    function Ninja() {
    this.skulk = function() { return this; }
    } var ninja1 = new Ninja();
    var ninja2 = new Ninja();
    • 构造器的目的是通过函数调用初始化创建新的对象。

函数调用方式差异

  • 函数调用方式之间点主要差异是:作为this参数传递给执行函数的上下文对象之间点区别。

    • 作为方法调用,该上下文是方法的拥有者;
    • 作为全局函数进行调用,其上下文永远是window(也就说,该函数是window的一个方法)。
    • 作为构造器进行调用,其上下文对象则是新创建的对象实例。

使用apply()和call()方法

  • 通过函数的apply()方法来调用函数,需要给apply()传入两个参数:一个是函数上下文的对象,另一个是作为函数参数所组成的数组;
  • 通过函数的call()方法来调用函数,需要给call()传入两个参数:一个是函数上下文的对象,另一个是作为函数参数的参数列表,而不是单个数组;
function juggle() {
var result = 0;
for (var n = 0; n < arguments.length; n++) {
result += arguments[n]
}
this.result = result;
}
var ninja1 = {};
var ninja2 = {}; juggle.apply(ninja1, [1,2,3,4]);
juggle.call(ninja2, 5,6,7,8) assert(ninja1.result === 10, 'juggled via apply');
assert(ninja2.result === 26, 'juggled via call');
  • 使用apply()和call()可以选择任意对象作为函数上下文;

函数总结

  • 函数是第一型对象;

    • 通过字面量进行创建。
    • 赋值给变量或属性。
    • 作为参数进行传递。
    • 作为函数结果进行返回。
    • 拥有属性和方法。
  • 函数是通过字面量进行创建的,其名称是可选的。
  • 在页面生命周期内,浏览器可以将函数作为各种类型的事件处理程序进行调用。
  • 变量的作用域开始于声明处,结束于函数尾部,其会跨域边界(如:大括号)
  • 内部函数在当前函数的任何地方都可用(提升),即便是提前引用。
  • 函数的形参列表和实际参数列表的长度可以是不同的。
    • 未赋值的参数被设置为undefined。
    • 多出的参数是不会绑定到参数名称的。
  • 每个函数调用都会传入两个隐式参数。
    • arguments,实际传入的参数集合。
    • this,作为函数上下文的对象引用。
  • 可以用不同的方法进行函数调用,不同的调用机制决定了函数上下文的不同。
    • 作为普通函数进行调用时,其上下文是全局对象(window)。
    • 作为方法进行调用时,其上下文是拥有该方法的对象。
    • 作为构造器进行调用时,其上下文是新分配的对象实例。
    • 通过函数的apply()或call()方法进行调用时,上下文可以设置成任意值。

匿名函数

  • 为了不让不必要的函数名称污染全局命名空间,可以创建大量的小型函数进行传递,而不是创建包含大量命令语句的大型函数。

递归

  • 递归:当函数调用自身,或调用另外一个函数,但这个函数的调用树中的某个地方又调用到了自己时,就产生了递归。

  • 递归的两个条件:引用自身,并且有终止条件。

闭包

  • 闭包是一个函数在创建时允许自身函数访问并操作该自身函数之外的变量时所创建的作用域

  • 闭包可以让函数访问所有的变量和函数,只要这些变量和函数存在于该函数声明时的作用域内就行。

var outerValue = 'ninja';

var later;

function outerFunction() {

    // 该变量的作用域限制在该函数内部,并且在函数外部访问不到;
var innerValue = 'samurai'; // 在外部函数内,声明一个内部函数。
// 注意:声明该函数时,innerValue是在作用域内的
function innerFunction() {
assert(outerValue, 'I can see the ninja');
assert(innerValue, 'I can see the samurai'); // 将内部函数引用到later变量上,由于later在全局作用域内,所以可以对它进行调用。
later = innerFunction;
}
} // 调用外部函数,将会声明内部函数,并将内部函数赋值给later变量。
outerFunction(); // 通过later调用内部函数。
// 我们不能直接调用内部函数,因为它的作用域(和innerValue一起)被限制在outerFunction内。
later();

闭包使用场景:私有变量

  • 在构造器内隐藏变量,使其在外部作用域不可访问,但是可以存在于闭包内。
function Ninja() {
var feints = 0; this.getFenits = function() {
return feints;
} this.feint = function() {
feints++;
}
} var ninja = new Ninja(); ninja.feint(); assert(ninja.getFenits() === 1, '调用一次,内部变量++'); assert(ninja.feints === undefined, '函数外部不可访问')
  • 变量的作用域依赖于变量所在的闭包

  • 闭包记住的是变量的引用,而不是闭包创建时刻该变量的值

原型与面向对象

  • 所有的函数在初始化时都有一个prototype属性,该属性的初始值是一个空对象。

  • 使用new操作符将函数作为构造器进行调用的时候,其上下文被定义为新对象的实例。

  • 在构造器内的绑定操作优先级永远高于在原型上的绑定操作优先级。因为构造器的this上下文指向的是实例自身,所以我们可以在构造器内对核心内容执行初始化操作。

  • 通过instanceof操作,可以判断函数是否继承了其原型链中任何对象的功能。

JavaScript函数、闭包、原型、面向对象的更多相关文章

  1. 理解javascript的闭包,原型,和匿名函数及IIFE

    理解javascript的闭包,原型,和匿名函数(自己总结) 一 .>关于闭包 理解闭包 需要的知识1.变量的作用域 例1: var n =99; //建立函数外的全局变量 function r ...

  2. javascript函数闭包(closure)

    一,首先感受下javascript函数的闭包 二,闭包 1,定义:闭包就是能够读取其他函数内部变量的函数,由于在javascript语言中,只有在函数内部的子函数才能够读取局部变量,因此可以把闭包简单 ...

  3. JavaScript 函数闭包的应用

    一.模仿块级作用域 JavaScript 没有块级作用域的概念,那么可以模拟像java中将很多变量私有化封装起来,保护数据,防止数据泄漏,封装细节,这样安全性和可控性更高 function box(c ...

  4. Javascript函数闭包详解(通俗易懂

    许多书上闭包过于复杂讲解难懂,自己理解了一下并总结啦~ 讲闭包之前,需要先明白以下几个概念. 总之,函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域. 1.执行上下文(executi ...

  5. JavaScript 函数闭包

    在函数中定义函数,这些定义的内部函数可以访问它们所在的外部函数中所有局部变量.参数以及声明的其它内部函数.当这样的内部函数在包含它们的外部函数之外被调用时就会形成闭包. 在没有class机制只有函数的 ...

  6. JavaScript函数——闭包

    闭包 概念 只有函数内部的子函数才能读取局部变量,所以闭包可以理解成"定义在一个函数内部的函数".在本质上,闭包是将函数内部和函数外部连接起来的桥梁 例子 function out ...

  7. Javascript函数闭包及案例详解

    什么情况下会形成闭包,什么是闭包 闭包(Closure):函数和其周围的状态(词法环境)的引用捆绑在一起形成闭包 可以在另一个作用域中调用一个函数的内部函数并访问到该函数的作用域中的成员 下面来看一个 ...

  8. 常用Javascript函数与原型功能收藏

    // 重复字符串 String.prototype.repeat = function(n) { return new Array(n+1).join(this); } // 替换全部 String. ...

  9. JavaScript碎片—函数闭包(模拟面向对象)

    经过这几天的博客浏览,让我见识大涨,其中有一篇让我感触犹深,JavaScript语言本身是没有面向对象的,但是那些大神们却深深的模拟出来了面向对象,让我震撼不已.本篇博客就是在此基础上加上自己的认知, ...

  10. JavaScript碎片———函数闭包(模拟面向对象)

    经过这几天的博客浏览,让我见识大涨,其中有一篇让我感触犹深,JavaScript语言本身是没有面向对象的,但是那些大神们却深深的模拟出来了面向对象,让我震撼不已.本篇博客就是在此基础上加上自己的认知, ...

随机推荐

  1. python之+=与+(转载)

    先看一个简单的例子 从程序分析,进行直接+操作后,python会重新生成一个对象,而进行+=操作并不改变原来的对象,是在原来对象的基础上进行操作,所以+=也称为就地加 除此之外+和+=还有不同: 从程 ...

  2. 【python】kafka在与celery和gevent连用时遇到的问题

    前提:kafka有同步,多线程,gevent异步和rdkafka异步四种模式.但是在与celery和gevent连用的时候,有的模式会出错. 下面是我代码运行的结果. 结论:使用多线程方式! 使用同步 ...

  3. cf1042d 树状数组逆序对+离散化

    /* 给定一个数组,要求和小于t的段落总数 求前缀和 dp[i]表示以第i个数为结尾的小于t的段落总数,sum[i]-sum[l]<t; sum[i]-t<sum[l],所以只要找到满足条 ...

  4. Eclipse编写ExtJS5卡死问题

    本篇以eclipse为例,导入后在编译时很容易出现eclipse的卡死现象,这主要是js文件的校验引起的. 我们可通过如下方法进行配置: 打开该项目的.project文件,删除如下配置即可: < ...

  5. Play框架--初学笔记

    目录结构 web_app 根目录 | sbt SBT Unix 批处理脚本用于启动sbt-launch.jar | sbt.bat SBT Windows 批处理脚本用于启动sbt-launch.ja ...

  6. idea svn配置报错:Can't use Subversion command line client:svn

    1. 在Intellij IDEA里checkout东西时出先这个错误提示:Can't use Subversion command line client:svnSubversion command ...

  7. Linux发布WebApi

    一:WebApi 使用Owin来做  http://www.cnblogs.com/xiaoyaodijun/category/666029.html 二:安装最新版的Jexus服务 https:// ...

  8. 基于容器的ETCD集群脚本

    其实是从上一篇的脚本里剥离出来的. 加深一下印象吧. docker run \ -d \ -p ${ETCD_CLI_PORT}:${ETCD_CLI_PORT} \ -p ${ETCD_CLU_PO ...

  9. GO语言标准库—命令行参数解析FLAG

    flag包是Go语言标准库提供用来解析命令行参数的包,使得开发命令行工具更为简单 常用方法 1.flag.Usage 输出使用方法,如linux下ls -h的帮助输出 2.flag.Type(参数名, ...

  10. C#学习-面向对象

    封装:把客观事物封装成类,并将类内部的实现隐藏,以保证数据的完整性: 比如年龄赋值为负数,就是个例子.当我们把类的字段定义为公共类型时,外部对象可以直接对类内部的数据进行操作,此时无法对这些操作进行一 ...