JavaScript面向对象小抄集

时间:2022-01-21 22:11:13

前言

本文旨在记录JavaScript中面向对象的基础知识

搞明白JavaScript中的面向对象

一切都是对象

JavaScript中,除了基本类型外,其它类型都是对象类型

  • 所谓对象就是若干属性的集合
    • 数组、函数等都是对象,对象还是对象。像Number、Array、Function等这些函数,就是JavaScript默认的内建对象,可以直接使用 ,因此也常说JavaScript是基于对象的语言
    • 对象里面的一切都是属性,方法也是属性,依然表示为键值对的形式
    • 函数也可以有对象属性,其中JQuery中的$就是一个函数,$.trim()中的trim()就是函数$中的一个方法属性
  • JavaScript中的类型分为两种
    • 值类型(非对象):undefined, number, string, boolean
    • 引用类型(对象): 函数、数组、对象、null、new Number(10)、new String("1")、new Array(1) 等
  • 判断对象方式:
    • 使用typeof: 当返回时"function"或者"object"字符串说明是引用类型对象
    • 使用instanceof:通过obj instanceOf Object 返回true判断对象
  • 所有对象都是继承自Object对象的 ,Object对象包含了一些通用的方法如 hasOwnProperty , isPrototypeOf

函数和对象的关系

对象都是由函数创建的,函数也是对象

  • 对象都是通过函数创建的
    • var obj = new Object() 语句中的Object就是构造函数
    • var obj = {} 这种字面量的创建方式其实是构造函数方式的语法糖而已,仅仅形式不同
  • 函数也是对象
    • Object、Array、Number都是(构造)函数,通过Number instanceof Object 返回true便可知
    • var MyFunction = function(){} 其中MyFunction也是对象,该对象是通过Function函数创建的

prototype(显式)原型

每个函数都有一个prototype属性,该属性也是一个对象,称之为(显式)原型,默认含有一个constructor属性,指向函数本身

JavaScript面向对象小抄集

constrcutor的作用探讨,请参见两篇博客

隐式原型

对比函数上的显式原型prototype,每一个对象上都有一个隐式原型__Proto__

  • __proto__都指向创建该对象的函数的prototype

JavaScript面向对象小抄集

  • 函数中的prototype也是一个对象,依然拥有__proto__属性。 自定义函数的prototype本质上就是和 var obj = {} 是一样的,都是被Object创建,所以它的__proto__指向的就是Object.prototypeObject.prototype是一个特例,它的__proto__指向的是null

    JavaScript面向对象小抄集

  • 函数也是一个对象,也是被创建出来的,因此也有__proto__属性,指向的是Function.prototype ,Function.prototype__proto__是Object创建的,因此指向了Object.prototype

JavaScript面向对象小抄集

  • Js中的自定义函数、内建对象Object、Number等,这些也都是函数,都是Function函数对象创建出来的,因此它们的__proto__都指向了Function.prototype。如Object.proto === Function.prototype//true。而Functiond函数,也是由Function函数创建出来,因此它的__proto__指向了Function.prototype

JavaScript面向对象小抄集

继承

传统的Javascript中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象 , ES6引入了Class(类)这个概念,通过class关键字可以定义类。该关键字的出现使得其在对象写法上更加清晰,更像是一种面向对象的语言 。这里只描述传统JS的继承实现,也就是ES6之前的版本

  • instanceOf 判断对象原理:Instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。Instanceof的判断规则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false

  • js的继承是基于原型链

    • 访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链
    • 判断属性是否来自该对象还是来自原型链上的对象,可以使用hasOwnProperty。所有对象都有hasOwnProperty方法,继承自Object.prototype
  • 继承的几种方式:

    1. 构造函数绑定

    2. prototype模式

    3. 直接继承prototype

    4. 利用空对象作为中介

    5. 拷贝继承

    6. object()方法

    以上关于继承的几种方式,可以参考阮一峰大神的三篇文章:

  • 对比ES5和ES6的继承方式,关于ES6的继承,可以参考文章:

    JavaScript面向对象小抄集

this关键字

this是上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境,this的取值都会有所不同

  • 全局 & 调用普通函数,this都指向window对象
  • 构造函数中,this就代表它即将new出来的对象,如果作为普通函数调用,则this就代表window对象
  • 函数作为对象的一个属性,此时this代指向的是这个对象本身
  • 函数用call或者apply调用,this的值就是传入的对象
  • 在函数的prototype的函数使用this,代表的是当前的对象

扩展

  1. 所谓构造函数就是用来new对象的函数。其实严格来说,所有的函数都可以new一个对象,但是有些函数的定义是为了new一个对象,而有些函数则不是。另外注意,构造函数的函数名第一个字母大写(规则约定)。例如:Object、Array、Function等

  2. call和apply的异同 :

    1. 都是在运行时改变当前函数中this的指向,其实就是改变该函数执行的上下文环境
    2. call在使用时候,可以有多个参数,其中第一个为上下文对象
    3. apply在使用的时候,只能有两个参数,其中第一个参数为上下文对象,第二个参数必须为数组
  3. callee和caller(这两个玩意少用) :

    • 关于caller:

      • caller 返回一个对函数的引用,该函数调用了当前函数

      • 对于函数来说,caller 属性只有在函数执行时才有定义。 如果函数是由 Javascript 程序的顶层调用的,那么 caller 包含的就是 null

      • 关联在函数上 functonName(函数名).caller

      • 注意:arguments.caller 已经不可使用了,但是你还可以使用 Function.caller

        function whoCalled() {
        if (whoCalled.caller == null)
        console.log('I was called from the global scope.');
        else
        console.log(whoCalled.caller + ' called me!');
        }
    • 关于 callee

      • 返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文,callee 属性的初始值就是正被执行的 Function 对象

      • 关联在arguments上 arguments.callee

      • callee拥有length属性,arguments.length是实参长度,arguments.callee.length是形参长度

        //1. callee可以打印其本身
        function calleeDemo() {
        alert(arguments.callee);
        } //2. 用于验证参数
        function calleeLengthDemo(arg1, arg2) {
        if (arguments.length==arguments.callee.length) {
        window.alert("验证形参和实参长度正确!");
        return;
        } else {
        alert("实参长度:" +arguments.length);
        alert("形参长度: " +arguments.callee.length);
        }
        } //3. 递归计算
        var sum = function(n){
        if (n < = 0)
        return 1;
        else
        return n +arguments.callee(n - 1)
        }

执行上下文

执行上下文也叫执行上下文环境

  • 执行上下文就是浏览器在执行运行js代码前做的准备工作,主要是三个点

    • 变量、函数表达式--变量声明,默认赋值为undefined;
    • this--赋值
    • 函数声明--赋值
  • js执行代码段之前都会通过“准备工作”来生成执行上下文。这个“代码段”其实分三种情况

    • 全局代码
    • 函数
      • 参数--赋值
      • arguments--赋值
      • *变量的取值作用域--赋值
    • eval代码

执行上下文栈

执行上下文就是JavaScript 在被解析和运行时环境的抽象概念,JavaScript 运行任何代码都是在执行上下文环境中运行的,执行上下文包括三个周期:创建——运行——销毁

  • 执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个

JavaScript面向对象小抄集

作用域

几乎所有的语言都有作用域的概念,简单的说,作用域就是变量和函数的可访问范围,即作用域控制在变量和函数的可见性和生命周期。

  • 在Javascript中,引擎、编译器和作用域共同协调完成Javascript的执行过程

  • Javascript没有块级作用域,所以我们在编写代码的时候,不要在“块”里面声明变量,要在代码的一开始就声明好了。以避免发生歧义

  • Javascript除了全局作用域之外,只有函数可以创建的作用域。所以,我们在声明变量时,全局代码要在代码前端声明,函数中要在函数体一开始就声明好。除了这两个地方,其他地方都不要出现变量声明。而且建议用“单var”形式

  • 作用域有上下级的关系,上下级关系的确定就看函数是在哪个作用域下创建的。例如,fn作用域下创建了bar函数,那么“fn作用域”就是“bar作用域”的上级

    JavaScript面向对象小抄集

  • 作用域有上下级的关系,上下级关系的确定就看函数是在哪个作用域下创建的。例如,fn作用域下创建了bar函数,那么“fn作用域”就是“bar作用域”的上级

  • 作用域最大的用处就是隔离变量。不同作用域下同名变量不会有冲突

    • 如jQuery源码的最外层是一个自动执行的匿名函数。在jQuery源码中,声明了大量的变量,这些变量将通过一个函数被限制在一个独立的作用域中,而不会与全局作用域或者其他函数作用域的同名变量产生冲突。全世界的开发者都在用jQuery,如果不这样做,很可能导致jQuery源码中的变量与外部javascript代码中的变量重名,从而产生冲突

      JavaScript面向对象小抄集

  • 作用域和执行上下文关系

    • 除了全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时确定
    • 作用域只是一个“地盘”,一个抽象的概念,其中没有变量。要通过作用域对应的执行上下文环境来获取变量的值。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。所以,**作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了。所以,如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值 **

*变量和作用域链

如我在全局中定义了一个变量a,然后我在函数中使用了这个a,这个a就可以称之为*变量,可以这样理解,凡是跨了自己的作用域的变量都叫*变量

JavaScript面向对象小抄集

  • 在A作用域中使用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,x就是一个*变量。如上程序中,在调用fn()函数时,函数体中第6行。取b的值就直接可以在fn作用域中取,因为b就是在这里定义的。而取x的值时,就需要到另一个作用域中取,要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”。这就是所谓的“静态作用域”
  • 上面描述的只是跨一步作用域去寻找。如果跨了一步,还没找到呢?——接着跨!——一直跨到全局作用域为止。要是在全局作用域中都没有找到,那就是真的没有了。这个一步一步“跨”的路线,我们称之为——作用域链

闭包

闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁--百度百科

  • 闭包的两种用法

    • 函数作为返回值

    JavaScript面向对象小抄集

    bar函数作为返回值,赋值给f1变量。执行f1(15)时,用到了fn作用域下的max变量的值

    • 函数作为参数被传递

    JavaScript面向对象小抄集

    fn函数作为一个参数被传递进入另一个函数,赋值给f参数。执行f(15)时,max变量的取值是10,而不是100

  • 闭包的用途

    • 读取函数内部的变量

    • 让变量的值始终保持在内存中

  • 注意点

    • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除
    • 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值
  • 闭包相关参考

参考

JavaScript面向对象小抄集的更多相关文章

  1. JavaScript设计模式小抄集&lpar;持续更新&rpar;

    前言 本文旨在记录JavaScript中常用的设计模式代码片段,简要说明使用场景,不过于追究细节.在设计模式开篇之前,还是先要搞清楚JavaScript中关于面向对象的基础知识,可以先看看JavaSc ...

  2. JavaScript—面向对象小例子

    什么是面向对象 要是以前别人问我.随口道来,封装继承多态,万物皆对象...一大推.说的自己都以为自己掌握了面向对象.呵呵一笑.确实掌握了 只是不会用..... 什么是面向对象编程 以前 学.Net 虽 ...

  3. 总结的git操作命令小抄集

    .gitignore 本地仓库主目录下,用于定义提交时忽略的文件   git add <file-name> 将修改或新增的文件存入暂存区   git reset HEAD <fil ...

  4. 《JavaScript面向对象编程指南(第2版)》读书笔记(二)

    <JavaScript面向对象编程指南(第2版)>读书笔记(一) <JavaScript面向对象编程指南(第2版)>读书笔记(二) 目录 一.基本类型 1.1 字符串 1.2 ...

  5. JavaScript学习笔记&lpar;三&rpar;——this、原型、javascript面向对象

    一.this 在JavaScript中this表示:谁调用它,this就是谁. JavaScript是由对象组成的,一切皆为对象,万物皆为对象.this是一个动态的对象,根据调用的对象不同而发生变化, ...

  6. JavaScript学习总结&lpar;三&rpar;——this、原型、javascript面向对象

    一.this 在JavaScript中this表示:谁调用它,this就是谁. JavaScript是由对象组成的,一切皆为对象,万物皆为对象.this是一个动态的对象,根据调用的对象不同而发生变化, ...

  7. 一个cheat命令 &equals;&equals; Linux命令小抄大全

    本文介绍一个Linux超级命令,有了这个命令,你就可以开开心心的使用linux上的各种命令了.当你要执行一个linux命令,在这个命令参数选项众多时,你一般怎么做?对,我们大多数人都会去求助man命令 ...

  8. JavaScript推荐资料合集&lpar;前端必看&rpar;

    这份合集覆盖了所有的JavaScript基本知识,从基本网络编程技巧,如变量.函数和循环语句,到高级一些的专题,如表单验证.DOM操作.客户端对象.脚本程序调试.学习前端的你不容错过! 资料名称 下载 ...

  9. 第一百零九节,JavaScript面向对象与原型

    JavaScript面向对象与原型 学习要点: 1.学习条件 2.创建对象 3.原型 4.继承 ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP).面向对象的语言有一个标 ...

随机推荐

  1. Java中怎样创建线程安全的方法

    面试问题: 下面的方法是否线程安全?怎样让它成为线程安全的方法? class MyCounter { private static int counter = 0; public static int ...

  2. LightOJ1170 - Counting Perfect BST(卡特兰数)

    题目大概就是求一个n个不同的数能构造出几种形态的二叉排序树. 和另一道经典题目n个结点二叉树不同形态的数量一个递推解法,其实这两个问题的解都是是卡特兰数. dp[n]表示用n个数的方案数 转移就枚举第 ...

  3. 3&period;4&period;2内核下的I2C驱动

    1. 框架1.1 硬件协议简介1.2 驱动框架1.3 bus-drv-dev模型及写程序a. 设备的4种构建方法a.1 定义一个i2c_board_info, 里面有:名字, 设备地址 然后i2c_r ...

  4. HDOJ&sol;HDU 1982 Kaitou Kid - The Phantom Thief &lpar;1&rpar;&lpar;字符串处理&rpar;

    Problem Description Do you know Kaitou Kid? In the legend, Kaitou Kid is a master of disguise, and c ...

  5. css 样式表 基础 样式

    1大小 width  宽度 height 高度 2 背景 background-color 背景色 background-image:url(图片位置) 背景图片 background-repeat: ...

  6. 【Android Developers Training】 1&period; 创建一个Android项目工程

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  7. js正则函数match、exec、test、search、replace、split使用介绍集合&comma;学习正则表达式的朋友可以参考下。

    match 方法 使用正则表达式模式对字符串执行查找,并将包含查找的结果作为数组返回. stringObj.match(rgExp) 参数 stringObj 必选项.对其进行查找的 String 对 ...

  8. FileWriter与BufferedWriter的适用场景

    IO这块,各种Writer,Reader,让人眼晕 而在网上基本找不到在什么时候用哪个类,并且网上的IO demo 很多用法都是错的 在这简单的分析一下FileWriter与BufferedWrite ...

  9. 9、RabbitMQ-集成Spring

    spring封装RabbitMQ看官网:https://spring.io/projects/spring-amqp#learn 开发时根据官网的介绍进行开发,具体的说明都有具体的声明 1.导入依赖 ...

  10. ZY、