Skip to content

Latest commit

 

History

History
1476 lines (1126 loc) · 49.8 KB

前端 · 第4周第1次 · JS学习-2· 2022_2_7.md

File metadata and controls

1476 lines (1126 loc) · 49.8 KB

一、内容结构

  • 对象
  • 函数
  • 构造函数
  • 作用域与变量提升
  • 数组
  • 包装对象
  • 内建对象
  • 原型对象
  • 异常与处理

二、文档及视频资源

w3school HTML 教程 w3school HTML 标签参考手册 MDN HTML 教程 MDN HTML 元素参考 w3school CSS 教程 w3school CSS 参考手册 MDN CSS 教程 MDN CSS 参考手册 w3school JS 教程 MDN JS 教程 阮一峰 JavaScript 教程 阮一峰 ECMAScript 6 教程 在线文档用于查阅参考资料

黑马程序员web前端开发HTML与CSS入门教程 尚硅谷Web前端零基础入门HTML5+CSS3基础教程 黑马程序员JavaScript入门教程 尚硅谷JavaScript基础入门完整版 在线视频学习资源用来辅助学习,帮助理解不太清楚的知识点

三、知识点总结

1. 对象

(1)简介

对象(object)是 JavaScript 语言的核心概念,也是最重要的数据类型。 简单说,对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合

var obj = {
  foo: 'Hello',
  bar: 'World' 
}; 
/*
上面代码中,大括号就定义了一个对象,它被赋值给变量obj,所以变量obj就指向一个对象
该对象内部包含两个键值对(又称为两个“成员”),第一个键值对是foo: 'Hello',其中foo是“键名”(成员的名称),字符串Hello是“键值”(成员的值)
键名与键值之间用冒号分隔
第二个键值对是bar: 'World',bar是键名,World是键值
两个键值对之间用逗号分隔。
*/

对象属于一种复合的数据类型,在对象中可以保存多个不同的数据类型 对象中的内容是一组键值对,其名称即键名,其值即键值,又被称为属性,对象是一组属性的无序集合 若对象的属性是一个函数,则称为方法

(2)分类
  • 内建对象
    • 由 ES 标准定义的对象,在任何 ES 实现中都可以使用
    • 比如:Math、String、Number等
  • 宿主对象
    • 由 JS 的运行环境提供的对象,一般主要指由浏览器提供的对象
    • 比如:DOM、BOM等
  • 自定义对象
    • 开发者自己创建的对象
(3)创建方式
  • new关键字:配合构造函数使用

后续第5节会介绍构造函数

  • 对象字面量
    • 键名默认是字符串,引号可加可不加
    • 若键名中有特殊字符,必须加引号
    • 最末尾引号可加可不加,出于兼容性考虑不加
// new 方式
let obj0 = new Object();

// 字面量方式
let obj1 = {
  // 可不加引号
  a: 12,
  // 可加引号
  'b': 'wz',
  // 出现特殊字符,必须加引号
  'c s q': true,
  // 可以是数值, 会自动转为字符串
  6: [],
  
  // 对象方法
  fun: function () {}
};
(4)对象属性操作
(i)属性读取
  • objectName.property
  • objectName["property"]

键值中出现特殊字符的基本上需要使用这种方法 若为数值可以忽略引号 允许使用表达式

  • objectName[expression]

expression 为变量,例如 var x = "age"; person[x];

var obj = {
  p: 'Hello World'
};
obj.p // "Hello World"
obj['p'] // "Hello World"

var foo = 'bar';
var obj = {
  foo: 1,
  bar: 2
};
obj.foo  // 1
obj[foo]  // 2
(ii)属性赋值
  • objectName.property = value
  • objectName["property"] = value

JS允许属性的“后绑定”,即可以在任意时刻新增属性

var obj = {};
obj.foo = 'Hello';
obj['bar'] = 'World';
(iii)属性删除
  • delete objectName.property

删除成功后返回true,否则返回false 注意:删除一个不存在的属性,delete不报错,而且返回true

(iv)判断属性是否存在
  • "propertyName" in objectName

in运算符用于检查对象是否包含某个属性,如果包含就返回true,否则返回false

  • objectName.hasOwnProperty('propertyName')

判断是否为对象自身的属性而不是继承的属性

(v)属性(键名)查看
  • Object.keys(obj);

查看一个对象本身的所有属性

var obj = {
  key1: 1,
  key2: 2
};
Object.keys(obj);    // ['key1', 'key2']
(vi)属性遍历
  • for (var i in obj) {}

for...in循环用来遍历一个对象的全部属性

  • 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性
  • 它不仅遍历对象自身的属性,还遍历对象继承的属性
var obj = {a: 1, b: 2, c: 3};
for (var i in obj) {
  console.log('键名:', i);
  console.log('键值:', obj[i]);
}
(5)基本数据类型与引用数据类型

JS 中基本变量都是保存在栈内存中存储 对象保存在堆内存中,变量保存的是对象在堆中的地址,即对象的引用

两个对象变量之间比较,比较的是对象的内存地址,因而对象无法比较 将对象变量赋值为 null,即变量存储的地址变为空,不会修改堆中对象数据

(6)对象与引用

如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址 修改其中一个变量,会影响到其他所有变量

var o1 = {};
var o2 = o1;
o1.a = 1;
o2.a   // 1
o2.b = 2;
o1.b   // 2

可以与C++和Java中的浅拷贝与深拷贝的区别进行比较

2. 函数 function

(1)简介

函数是一类特殊的对象,功能用法可类比于 C 语言函数 用 typeof 关键字检查类型,返回 "function"

(2)函数创建
  • 函数声名
function print(s) {
  console.log(s);
}

需要注意的是,函数声明存在变量提升现象

  • 函数表达式
var print = function(s) {
  console.log(s);
};
  • Function构造函数

var a = new Function(); 不推荐使用

重复声明函数:后面函数会覆盖前面函数的声明

(3)函数参数
(i)形参(parameter)

函数声名时声名的参数叫做形参,声名形参就相当于在函数内部声名了对应的变量,但是没赋值,默认值为 undefined

(ii)实参(argument)

在调用函数时,可以传入实参,实参会赋值给函数中对应的形参

(iii)参数规则
  • 无需规定参数类型,js运行时会自动确定参数类型
  • 可以传入任何类型的参数,对象、数组、函数均可
  • 实参个数可以少于形参个数

此时剩余形参不会赋值,默认为undefined

  • 实参个数可以超过形参个数

多余实参不会赋值给形参,可以通过函数内置的 arguments 对象获取

  • 基本数据类型是值传递

在函数体内修改参数值,不会影响到函数外部

  • 对象是引用传递

在函数内部修改参数,将会影响到原始值

  • 参数出现重名情况

仅最后一个参数有效

(4)函数返回值

用 return 语句返回函数执行结果,默认返回 undefined 调用函数的结果就是函数返回值 函数返回值可以是任何类型的值

(5)函数调用

函数被定义时,其内部代码不会执行;函数被调用时,其内部代码才会被执行 fun()

(6)函数属性
(i)name属性

返回函数的名字

(ii)length属性

返回函数的形参个数

(iii)tostring()方法

返回函数的源码

Math.sqrt.name    \\ sqrt
Math.sqrt.length    \\ 1
Math.sqrt.toString()    \\ function sqrt() { [native code] }
(7)函数其他概念
(i)arguments对象

arguments对象包含了函数运行时的所有参数,可以通过arguments对象的length属性判断函数调用时到底带几个参数,可以使用[ ]下标访问其的成员

var f = function (one) {
  console.log(arguments[0]);
  console.log(arguments[1]);
  console.log(arguments[2]);
}
f(1, 2, 3)
// 1
// 2
// 3

arguments对象带有一个callee属性,返回它所对应的原函数 这个属性在严格模式里面是禁用的,因此不建议使用

var f = function () {
  console.log(arguments.callee === f);
}
f() // true
(ii)立即执行函数 IIFE

在定义函数之后,立即调用该函数

  • 不必为函数命名,避免了污染全局变量
  • 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量
(function () {
  var tmp = newData;
  processData(tmp);
  storeData(tmp);
}());
(iii)eval命令

eval命令接受一个字符串作为参数,并将这个字符串当作语句执行 eval('var a = 1;'); eval的本质是在当前作用域之中,注入代码,由于安全风险和不利于 JavaScript 引擎优化执行速度,一般不推荐使用

3. 数组 Array

(1)简介

数组(array)是按次序排列的一组值,每个值的位置都有编号(从0开始),整个数组用方括号表示 任何类型的数据,都可以放入数组 同一个数组中可以存在多种不同的值 数组本质是一类特殊的对象,其键名是自0开始的正整数 数组中各项被称为数组的元素

(2)创建方法
  • new 关键字

var arr = new Array(); 不推荐使用

  • 使用数组文本

var arr = [item1, item2] 数组中的元素可以是任意数据类型,也可以是一个数组 最后一个元素后建议不要写逗号

(3)数组操作
(i)访问数组元素
  • array[index]
  • forEach() 方法、map()方法即数组遍历
(ii)添加数组元素
  • array[array.length] = item1';
  • push()、unshift() 方法
(iii)删除数组元素
  • delete 运算符,会在数组中留下空洞
  • pop() 、shift()、splice()、slice()方法,不会留下空洞
(4)数组length属性与in运算符

可通过 length 属性得到数组长度 可以设置数组length属性,但只能为非负整数

可以令数组length为0从而实现清空数组

同样的in运算符亦可以使用于数组,但不常用

(5)数组方法
(i)自开头结尾的添加或删除元素方法
  • push()

向数组末尾添加一个或多个新元素,并返回数组新的长度

  • pop()

从数组中删除最后一个元素,返回被删除的元素

  • unshift()

向数组开头添加一个或多个元素,返回数组新的长度

  • shift()

从数组中删除第一个元素,返回被删除的元素 以上方法都会_直接改变原数组_

(ii)基于数组下标的添加删除元素方法
  • slice(start, end)

不改变原数组 start 为开始索引位置,end 为终止索引位置 从数组的某个片段截取出新数组并返回,若 end 省略会截取至结尾,参数允许是负值此时会反向截取

  • splice(start, num, ele...)

改变原数组 start 为开始索引位置,num 表示删除元素个数,ele 及以后表示自动插入到开始索引前的元素 删除数组中指定元素,并将被删除元素以数组形式返回

let fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
console.log(fruits.slice(1,3));		// ['Orange','Lemon']
console.log(fruits.slice(-3,-1));		// ['Lemon','Apple']
console.log(fruits.slice(-3));			// ['Lemon','Apple','Mango']

let fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.splice(2,0,"Lemon","Kiwi");		// ['Banana','Orange','Lemon','Kiwi','Apple','Mango']
fruits.splice(2,2);		// ['Banana','Orange']
(iii)数组的遍历方法
  • forEach()

按顺序为数组中的每个元素调用一次函数

  • map()

按顺序为数组中的每个元素调用一次提供的函数 使用为每个数组元素调用函数的结果创建新数组

  • filter()

按顺序为数组中的每个元素调用一次提供的函数,判断各元素是否通过测试 返回包含所有符合条件的数组元素的数组 以上方法都_不会改变原数组_

(iv)数组检索
  • indexOf()

返回数组中符合条件的第一个元素的索引,未查找到返回-1

  • lastIndexOf()

返回数组中符合条件的倒数第一个元素的索引,未查找到返回-1

  • find()

返回数组中符合条件的第一个元素的值,未查找到返回undefined

  • findIndex()

返回数组中符合条件的第一个元素的索引,未查找到返回-1

  • some()

检查数组中的任何元素是否满足条件 如果找到函数返回 true 值的数组元素,some() 返回true(并且不检查剩余值) 否则返回 false

  • every()

检查数组中的所有元素是否都满足条件 如果找到函数返回 false 值的数组元素,every() 返回 false(并且不检查剩余值) 否则返回 true

  • includes()

确定数组是否包含指定的元素,返回一个布尔值 以上方法都_不会改变原数组_

(v)其他
  • Array.isArray(value)

返回一个布尔值,表示传入参数是否为数组

  • concat()

不改变原数组 连接两个或多个数组,将新的数组返回

  • join()

不改变原数组 将数组转换为一个字符串,并返回 可以接受一个字符串作为参数,该字符串会成为数组中元素的连接符,默认是逗号

  • reverse()

改变原数组 反转数组

  • sort()

改变原数组 默认按 Unicode 编码排序,因此对于数字排序不符合预期 sort() 方法可传入一个比较函数自定义排序规则 函数需定义两个形参 浏览器会分别使用数组中的元素作为实参调用函数 根据回调函数的返回值来决定元素排序,如果返回值大于 0 ,则交换位置 更多方法可参考 W3school Array 参考手册

(6)数组遍历
  • for 循环遍历
  • for-in 循环

同对象的情况,不推荐使用

  • forEach() 方法

需要一个函数作为参数,称这种函数为回调函数 第一个参数为当前遍历对象,第二个参数为当前遍历索引,第三个参数为当前遍历的数组 会改变原数组

  • map() 方法

使用情况和 forEach 相同 不同的是,forEach 方法不会返回值,map 方法会返回一个新的数组,且不会改变原数组

4. 变量作用域及创建变量

(1)创建变量推荐方法

JavaScript 提供原始数据类型字符串、数字和布尔的对象版本,但是并无理由创建复杂的对象,通过原始值执行速度快得多也更好用

  • 使用对象字面量 {} 代替 new Object()
  • 使用字符串字面量 "" 代替 new String()
  • 使用数值字面量代替 Number()
  • 使用布尔字面量代替 new Boolean()
  • 使用数组字面量 [] 代替 new Array()
  • 使用模式字面量代替 new RexExp()
  • 使用函数表达式 () {} 代替 new Function()
(2)作用域

(i)全局作用域

  • 函数之外声名的变量,会成为全局变量。其作用域是全局的,网页上所有脚本或函数都能访问
  • 全局作用域在页面打开时创建,页面关闭时销毁
  • 在 HTML 中,全局作用域是 window 对象,代表浏览器窗口,所有全局变量均作为 window 的属性保存
(ii)函数作用域
  • 函数中声名的变量,会成为函数的局部变量。其作用域是局部的,只能在函数内访问
  • 函数作用域在调用函数时创建,在函数执行完毕后销毁
  • 在函数作用域中可以访问到全局变量
  • 函数作用域中使用变量,先在函数作用域找,再到全局作用域找
(iii)块状作用域
  • let或const声明的变量拥有块状作用域,只能在相应的块状作用域内使用
  • 一般块状作用域是变量所在的{}覆盖的范围

5. 面向对象编程

(1)简介

面向对象编程(Object Oriented Programming,缩写为 OOP)是目前主流的编程范式。它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟 每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。对象可以复用,通过继承机制还可以定制。因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目

  • 对象是单个实物的抽象

一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个远程服务器连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。

  • 对象是一个容器,封装了属性(property)和方法(method)

属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,使用“属性”记录具体是哪一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息等等) 面向对象三大特性 - 封装、继承、多态

关于OOP可以参考后端部分的Java中关于面向对象编程的介绍 _我个人觉得后端部分介绍面向对象概念挺不错的 _链接

(2)构造函数
(i)简介

典型的面向对象编程语言(比如 C++ 和 Java),都有“类”(class)这个概念。所谓“类”就是对象的模板,对象就是“类”的实例;但是,JavaScript 语言的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype) PS:ES6后亦引入了类的方法,我们可以像其他语言一样使用class关键字创建类模板 JavaScript 语言使用构造函数(constructor)作为对象的模板。所谓”构造函数”,就是专门用来生成实例对象的函数;它就是对象的模板,描述实例对象的基本结构;一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构

(ii)特点
  • 构造函数与普通的函数区别不大
  • 为了与普通函数区别,构造函数名字的第一个字母通常大写
  • 函数体内部使用了this关键字,代表了所要生成的对象实例
  • 生成对象的时候,必须使用new命令
var Vehicle = function () {
  this.price = 1000;
};

var v = new Vehicle();
v.price // 1000
(iii)new关键字

new命令的作用,就是执行构造函数,返回一个实例对象 new命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号,但推荐带括号

new命令创建对象过程:

  1. 创建一个空对象,作为将要返回的对象实例
  2. 将这个空对象的原型,指向构造函数的prototype属性
  3. 将这个空对象赋值给函数内部的this关键字
  4. 开始执行构造函数内部的代码
  5. 返回新建的对象

可以结合下面的代码理解

function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {
  // 将 arguments 对象转为数组
  var args = [].slice.call(arguments);
  // 取出构造函数
  var constructor = args.shift();
  // 创建一个空对象,继承构造函数的 prototype 属性
  var context = Object.create(constructor.prototype);
  // 执行构造函数
  var result = constructor.apply(context, args);
  // 如果返回结果是对象,就直接返回,否则返回 context 对象
  return (typeof result === 'object' && result != null) ? result : context;
}

// 实例
var actor = _new(Person, '张三', 28);

特别注意的是,若return后面跟着一个对象,new命令会返回return语句指定的对象而不是this指向的对象;若对普通函数(内部没有this关键字的函数)使用new命令,则会返回一个空对象

解决_未使用new命令直接调用构造函数情况_

  1. 构造函数内部使用严格模式,即第一行加上"use strict"指令
function Fubar(foo, bar){
  'use strict';
  this._foo = foo;
  this._bar = bar;
}

Fubar()
// TypeError: Cannot set property '_foo' of undefined
  1. 构造函数内部判断是否使用new命令,如果发现没有使用,则直接返回一个实例对象
function Fubar(foo, bar) {
  if (!(this instanceof Fubar)) {
    return new Fubar(foo, bar);
  }

  this._foo = foo;
  this._bar = bar;
}
  1. 使用new.target属性,判断函数调用的时候,是否使用new命令
function f() {
  if (!new.target) {
    throw new Error('请使用 new 命令调用!');
  }
  // ...
}

f() // Uncaught Error: 请使用 new 命令调用!
(3)其他创建对象方法
(i)工厂函数

工厂函数是通过在函数内部显式地创建对象,然后返回对象

function createPerson(name, age, sex){
  let obj = {};
  obj.name = name;
  obj.age = age;
  obj.sex = sex;
  
  obj.sayName = function () {
    console.log(this.name);
  }
  
  return obj;
}

let per1 = createPerson("孙悟空", 18, "男");

工厂函数只能创建单一类型的对象,无法区分开不同类型的对象 构造函数是可以创建特定类型的对象,并且能够区分不同类型的对象

(ii)Object.create() 方法

以这个现有的对象作为模板,生成新的实例对象

let person1 = {
  name: '张三',
  age: 38,
  greeting: function() {
    console.log('Hi! I\'m ' + this.name + '.');
  }
};

let person2 = Object.create(person1);
// 对象person1是person2的模板,后者继承了前者的属性和方法
(4)this关键字
(i)简介

this 关键字指向的是它所属的对象 解析器在调用函数时会向函数内部传递一个隐含的参数 this 需要注意的是,this的指向是动态的,常常难以事先确定到底指向哪个对象

(ii)this 取值
  • 全局环境下,this 指的是全局对象 window
  • 在构造函数中,this 指的是实例对象即构造函数创建的对象
  • 在对象内的方法中,this 指的是方法所有者即其所在的对象
  • 在函数中,this 指的是全局对象 window,(严格模式下)this是 undefined
  • 在事件中,this指的是接收事件的元素
  • call() 和 apply() 方法中将 this 引用的是指定对象
(iii)this 使用注意点
  • 避免多层套用this
  • 避免数组处理方法如map和foreach中使用 this
  • 避免回调函数中使用this
(iv)固定this指向方法
  • call Function.prototype.call()

函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数

let n = 123;
let obj = { n: 456 };

function a() {
  console.log(this.n);
}

a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456

call方法还可以接受多个参数,call的第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数

function add(a, b) {
  return a + b;
}
add.call(this, 1, 2) // 3

call方法可以用于_调用对象的原生方法_

let obj = {};
obj.hasOwnProperty('toString') // false
obj.hasOwnProperty = function () {
  return true;
}; // 覆盖掉继承的 hasOwnProperty 方法
obj.hasOwnProperty('toString') // true
Object.prototype.hasOwnProperty.call(obj, 'toString') // false
  • apply

与call方法类似 特别的,原函数的参数,在call方法中必须依序添加,但是在apply方法中,必须以数组形式添加

/* 找出数组最大元素 */
let a = [10, 2, 4, 15, 9];
Math.max.apply(null, a) // 15

/* 将数组的空元素变为undefined */
Array.apply(null, ['a', ,'b'])  // [ 'a', undefined, 'b' ]

/* 转换类似数组的对象 */
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]

/* 绑定回调函数的对象 */
let o = new Object();
o.f = function () {
  console.log(this === o);
}
let f = function (){
  o.f.apply(o);
  // 或者 o.f.call(o); 亦可
};
  • bind

bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数 bind方法的参数就是所要绑定this的对象,返回值是一个新函数

var d = new Date();
d.getTime() // 1481869925657

var print = d.getTime;
print() // Uncaught TypeError: this is not a Date object.

var print = d.getTime.bind(d);
print() // 1481869925657

bind()还可以接受更多的参数,将这些参数绑定原函数的参数

var add = function (x, y) {
  return x * this.m + y * this.n;
}
var obj = {
  m: 2,
  n: 2
};
var newAdd = add.bind(obj, 5);
newAdd(5) // 20
(5)instanceof 运算符

instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例,其会检测对象的整个生成链 特别的,所有非null对象都是 Object 对象的后代,因此判断Object的结果恒真

var d = new Date();
d instanceof Date // true
d instanceof Object // true
(6)原型对象 prototype
(i)原型对象概念

每个函数都会创建一个 prototype 属性,这个属性是一个对象,是通过调用构造函数创建的对象的原型,在上面定义的属性和方法可以被对象实例共享 原型对象的属性不是实例对象自身的属性,只要修改原型对象,变动就立刻会体现在所有实例对象上

function Animal(name) {
  this.name = name;
}
Animal.prototype.color = 'white';

var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');

cat1.color // 'white'
cat2.color // 'white'

原型对象的作用,就是定义所有实例对象共享的属性和方法,而实例对象可以视作从原型对象衍生出来的子对象 image.png

(ii)原型链 prototype chain

JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”:对象到原型,再到原型的原型…… 如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性。也就是说,所有对象都继承了Object.prototype的属性。而Object.prototype的原型是null。因此,原型链的尽头就是null 可以利用 Object.getPrototypeOf( ) 方法返回参数对象的原型和Object.setPrototypeOf( ) 修改参数对象的原型

(iii)proto 属性

后代实例可以通过隐含的 proto 属性来访问其继承的原型对象,也可以直接对原型的属性或方法进行修改,使构造函数的每个实例都能同步

(iv)对象与原型层级

使用一个函数的属性或方法时,搜索过程如下

  • 自身有,直接使用
  • 自身没有,在原型对象中寻找,如果原型对象中有,则使用
  • 如果原型对象没有,继续查找原型的原型,直到 Object 对象的原型
  • Object 原型没有原型,如果没有找到,返回 undefined
(v)constructor 属性

constructor属性,默认指向prototype对象所在的构造函数 作用是,判断实例对象被哪个构造函数产生;亦可以从一个实例对象新建另一个实例

(7)封装
var module1 = (function () {
 var _count = 0;
 var m1 = function () {
   //...
 };
 var m2 = function () {
  //...
 };
 return {
  m1 : m1,
  m2 : m2
 };
})();

非常不直观也不好用,后面会介绍利用class关键字实现效果

(8)继承
/* 继承 */
function Shape() {
  this.x = 0;
  this.y = 0;
}
Shape.prototype.move = function (x, y) {
  this.x += x;
  this.y += y;
  console.info('Shape moved.');
};

// 第一步,子类继承父类的实例
function Rectangle() {
  Shape.call(this); // 调用父类构造函数
}
// 另一种写法
function Rectangle() {
  this.base = Shape;
  this.base();
}

// 第二步,子类继承父类的原型
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();
rect instanceof Rectangle  // true
rect instanceof Shape  // true

同上,不够直观方便

6. 内建对象简介

关于以下内容,个人认为不需要都记住,只需要理解其原理,在需要使用时查阅资料即可

(1)包装对象
(i)概述

三种原始类型的值——数值、字符串、布尔值——拥有对应的原始类型的“包装对象”(wrapper)

  • Number
  • String
  • Boolean

不推荐显式使用 浏览器会隐式使用,基本数据类型的值来调用属性和方法(toString()等),浏览器会临时使用包装类将其转换为对象,然后调用相应对象的属性和方法,调用完之后会转换回基本数据类型

(ii)共有方法属性
  • valueOf()方法返回包装对象实例对应的原始类型的值
  • toString()方法返回对应的字符串形式
(iii)Boolean对象

Boolean对象是布尔值对应的包装对象,可以作为构造函数使用,也可以作为工具函数使用

(iv)Number对象

Number对象是数值对应的包装对象,可以作为构造函数使用,也可以作为工具函数使用 静态属性: Number.POSITIVE_INFINITY:正的无限,指向Infinity Number.NEGATIVE_INFINITY:负的无限,指向-Infinity Number.NaN:表示非数值,指向NaN Number.MIN_VALUE:表示最小的正数(即最接近0的正数),即5e-324 Number.MAX_SAFE_INTEGER:表示能够精确表示的最大整数,即9007199254740991 Number.MIN_SAFE_INTEGER:表示能够精确表示的最小整数,即-9007199254740991 实例方法

  • toString()

用来将数值转为字符串形式 可以接受一个参数,表示输出的进制,默认转为10进制 toString方法只能将十进制的数,转为其他进制的字符串;如果要将其他进制的数,转回十进制,需要使用parseInt方法

  • toFixed()

将数值转为指定位数的小数对应的字符串 接受一个参数,为小数点后的位数,有效范围为0到100

  • toExponential()

用于将数值转为科学计数法形式的字符串 接受一个参数,为小数点后的位数,有效范围为0到100

  • toPrecision()

将数值转为指定位数的有效数字的字符串 接受一个参数,为小数点后的位数,有效范围为0到100

  • toLocaleString()

将当前数值转为在该地区的当地书写形式的字符串 第一个参数接受一个地区码(必需),第二个参数配置对象用来定制指定用途(该对象的style属性指定输出样式,默认值是decimal表示输出十进制形式;值为percent表示输出百分数;值为currency则可以结合currency属性表示指定格式的货币)

(2)字符串String对象
(i)简介

String对象是字符串对应的包装对象 字符串对象是一个类似数组的对象 可以用[ ]访问字符串各项

(ii)属性
  • length属性

返回字符串的长度

(iii)方法
  • charAt()

根据索引获取指定字符 若参数为负数或大于等于字符串的长度返回空字符串

  • charCodeAt()

根据索引获取指定字符的编码(Unicode 编码) 若参数为负数或大于等于字符串的长度返回NaN

  • String.fromCharCode()

通过 String 构造方法调用 根据字符编码获取字符

  • concat()

连接多个字符串

  • indexOf()

确定一个字符串在另一个字符串中第一次出现的位置 如果含有,返回其第一次出现的索引 如果不含有,返回-1 可以指定第二个参数,指定开始查找的位置

  • lastIndexOf()

和 indexOf() 一样用法 不同之处在于 lastIndexOf() 从后往前找 可以指定第二个参数,指定开始查找位置,指定也是从前面开始第几个

  • slice()

从原字符串取出子字符串并返回 第一个参数是子字符串的开始位置,第二个参数是子字符串的结束位置(不含该位置) 若省略第二个参数,则表示子字符串一直到原字符串结束 若参数是负值,表示从结尾开始倒数计算的位置,即该负值加上字符串长度 若第一个参数大于第二个参数(正数情况下),返回空串

  • substring()

从原字符串取出子字符串并返回 第一个参数表示开始位置索引,第二个参数表示结束位置索引 用法和数组的 slice 方法类似 不能传入负值,如果传入负值,默认使用 0 会自动调整参数位置,如果第二个参数小于第一个,则交换位置

  • substr()

从原字符串取出子字符串并返回 第一个参数表示开始位置索引,第二个参数表示截取长度 若省略第二个参数,则表示子字符串一直到原字符串的结束 若第一个参数是负数,表示倒数计算的字符位置 若第二个参数是负数,将被自动转为0,即返回空字符串

  • split()

根据给定规则分割字符串返回一个数组 可传入一个字符串参数,根据该字符串拆分数组 若省略参数,则返回数组的唯一成员就是原字符串 若分割规则为空字符串,则返回数组的成员是原字符串的每一个字符 可传入第二个参数,表示返回数组的成员数

  • trim()

去除字符串两端的空格返回一个新字符串 会去除的不仅是空格,还包括制表符(\t、\v)换行符(\n)和回车符(\r)

  • toUpperCase()

将字符串转换为大写并返回新字符串

  • toLowerCase()

将字符串转换为小写并返回新字符串

  • localCompare()

比较两个字符串,返回一个整数 若小于0,表示第一个字符串小于第二个字符串 若等于0,表示两者相等 若大于0,表示第一个字符串大于第二个字符串 遵从自然语言的顺序比较,与运算符比较结果可能不同 可传入第二个参数,指定所使用的语言(默认是英语)

  • match

确定原字符串是否匹配某个子字符串 传入一个字符串参数,用于匹配 若查找到返回一个数组,成员为匹配的第一个字符串 若没有找到匹配,则返回null 返回数组有index属性和input属性分别表示匹配字符串开始的位置和原始字符串

  • search

基本类似match方法 传入一个字符串参数,用于匹配 若查找到返回匹配的第一个位置下标 若没有找到匹配,则返回-1

  • replace

查找并替换匹配的子字符串,一般情况下只替换第一个匹配 传入一个字符串参数,用于匹配

(3)Math 对象

Math是 JavaScript 的原生对象,提供各种数学功能 该对象不是构造函数,不能生成实例,所有的属性和方法都必须在Math对象上调用

(i)属性
  • Math.E:常数e
  • Math.LN2:2 的自然对数
  • Math.LN10:10 的自然对数
  • Math.LOG2E:以 2 为底的e的对数
  • Math.LOG10E:以 10 为底的e的对数
  • Math.PI:常数π,圆周率
  • Math.SQRT1_2:0.5 的平方根
  • Math.SQRT2:2 的平方根
(ii)方法
  • Math.abs():绝对值
  • Math.ceil():向上取整
  • Math.floor():向下取整
  • Math.max():最大值
  • Math.min():最小值

若参数为空, Math.min返回Infinity, Math.max返回-Infinity

  • Math.pow():幂运算

第一个参数为底数、第二个参数为指数

  • Math.sqrt():平方根

若参数是一个负值,则返回NaN

  • Math.log():自然对数
  • Math.exp():e的指数
  • Math.round():四舍五入
  • Math.random():随机数

返回0到1之间的一个伪随机数,可能等于0,但是一定小于1

// 任意范围的随机数生成函数
function getRandomArbitrary(min, max) {
  return Math.random() * (max - min) + min;
}

// 任意范围的随机整数生成函数
function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
  • Math.sin():返回参数的正弦
  • Math.cos():返回参数的余弦
  • Math.tan():返回参数的正切

以上参数为弧度值

  • Math.asin():返回参数的反正弦
  • Math.acos():返回参数的反余弦
  • Math.atan():返回参数的反正切

以上返回值为弧度值

(4)Date 对象

Date对象是 JavaScript 原生的时间库 以国际标准时间(UTC)1970年1月1日00:00:00作为时间的零点,可以表示的时间范围是前后各1亿天(单位为毫秒)

(i)普通函数调用与构造函数调用

作为普通函数直接调用,返回一个代表当前时间的字符串 Date() // "Tue Dec 01 2015 09:34:43 GMT+0800 (CST)" 作为构造函数通过new关键字调用 返回一个字符串代表实例封装对应的时间

  • new Date()

创建的时间对象默认封装当前时间

  • new Date(dateString)

从日期字符串创建一个新的时间对象 日期字符串格式:"月/日/年 时: 分: 秒"

  • new Date(year, month, ...)

传入 7 个数字参数:年、月、日、小时、分钟、秒、毫秒 传入 5 个数字参数:年、月、日、小时、分钟 传入 4 个数字参数:年、月、日、小时 传入 3 个数字参数:年、月、日 传入 2 个数字参数:年、月 传入 1 个数字参数:毫秒

月份从 0 到 11 参数可以是负整数,代表1970年元旦之前的时间 能被Date.parse()方法解析的字符串,都可以当作参数 超出了正常范围的参数会被自动折算

(ii)运算

两个日期实例对象进行减法运算时,返回的是它们间隔的毫秒数 进行加法运算时,返回的是两个字符串连接而成的新字符串

(iii)方法
  • Date.now()

获取当前时间戳,即当前时间距离时间零点(1970年1月1日 00:00:00 UTC)的毫秒数

  • Date.parse()

解析日期字符串,返回该时间距离时间零点(1970年1月1日 00:00:00)的毫秒数 解析失败,返回NaN

  • Date.UTC()

接受年、月、日等变量作为参数,返回该时间距离时间零点(1970年1月1日 00:00:00 UTC)的毫秒数 参数规则类似于Date()构造函数 区别在于Date.UTC方法的参数,会被解释为 UTC 时间(世界标准时间),Date构造函数的参数会被解释为当前时区的时间

  • getDate()

获取当前日期是几日

  • getDay()

获取当前日期是星期几 返回值为 0 - 6,0 表示周日、1 表示周一 ...

  • getMonth()

获取当前日期月份 返回值为 0 - 11,0 表示一月、1 表示二月 ...

  • getFullYear()

返回当前日期年份

  • getHours()、getMinutes()、getSeconds()、getMillseconds()

返回当前日期的小时,分钟,秒数,毫秒数

  • getTime()

获取当前日期对象的时间戳 时间戳,指的是从格林威治标准时间的 1970 年 1 月 1 日到当前时间的毫秒数 计算机底层保存时间使用的都是时间戳

  • setDate(date):设置实例对象对应的每个月的几号(1-31),返回改变后毫秒时间戳
  • setFullYear(year [, month, date]):设置四位年份
  • setHours(hour [, min, sec, ms]):设置小时(0-23)
  • setMilliseconds():设置毫秒(0-999)
  • setMinutes(min [, sec, ms]):设置分钟(0-59)
  • setMonth(month [, date]):设置月份(0-11)
  • setSeconds(sec [, ms]):设置秒(0-59)
  • setTime(milliseconds):设置毫秒时间戳

基本与相应的get方法对应

  • toString方法返回一个完整的日期字符串
  • toUTCString方法返回对应的 UTC 时间,也就是比北京时间晚8个小时的时间
  • toDateString方法返回日期字符串(不含小时、分和秒)
  • toTimeString方法返回时间字符串(不含年月日)
  • toISOString方法返回对应时间的 ISO8601 写法,toJSON方法返回一个符合 JSON 格式的 ISO 日期字符串,与toISOString方法的返回结果完全相同
(5)Object 对象

JS 的所有其他对象都继承自Object对象,即那些对象都是Object的实例

(i)构造方法Object()

可以当作工具方法使用,将任意值转为对象 若参数为空(或者为undefined和null),返回值为一个空对象 若参数是原始类型的值,会转为对应的包装对象的实例 若参数是一个对象,它总是返回该对象,即不会转换

// 判断变量是否为对象的函数
function isObject(value) {
  return value === Object(value);
}

可以当作构造函数使用,即前面可以使用new命令创建新对象 可以接受一个参数,若该参数是一个对象,则直接返回这个对象;若该参数是一个原始类型的值,则返回该值对应的包装对象

(ii)Object对象静态方法

遍历对象属性

  • Object.keys():遍历对象的属性,仅返回可枚举的属性
  • Object.getOwnPropertyNames():遍历对象的属性,会返回可枚举与不可枚举的属性

对象属性模型

  • Object.getOwnPropertyDescriptor():获取某个属性的描述对象
  • Object.defineProperty():通过描述对象,定义某个属性
  • Object.defineProperties():通过描述对象,定义多个属性

控制对象状态

  • Object.preventExtensions():防止对象扩展
  • Object.isExtensible():判断对象是否可扩展
  • Object.seal():禁止对象配置
  • Object.isSealed():判断一个对象是否可配置
  • Object.freeze():冻结一个对象
  • Object.isFrozen():判断一个对象是否被冻结

原型链

  • Object.create():该方法可以指定原型对象和属性,返回一个新的对象
  • Object.getPrototypeOf():获取对象的Prototype对象
(iii)Object对象实例方法
  • Object.prototype.valueOf():返回当前对象对应的值
  • Object.prototype.toString():返回当前对象对应的字符串形式
  • Object.prototype.toLocaleString():返回当前对象对应的本地字符串形式
  • Object.prototype.hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性
  • Object.prototype.isPrototypeOf():判断当前对象是否为另一个对象的原型
  • Object.prototype.propertyIsEnumerable():判断某个属性是否可枚举

7. 异常与错误处理机制

(1)Error实例对象

JavaScript 解析或运行时,一旦发生错误,引擎就会抛出一个错误对象 JavaScript 原生提供Error构造函数,所有抛出的错误都是这个构造函数的实例 Error对象拥有如下属性

  • message:错误提示信息
  • name:错误名称(非标准属性)
  • stack:错误的堆栈(非标准属性)
(2)原生错误类型
  • SyntaxError对象

解析代码时发生的语法错误

  • ReferenceError对象

引用一个不存在的变量时或将一个值分配给无法分配的对象时发生的错误

  • RangeError对象

一个值超出有效范围时发生的错误

  • TypeError对象

变量或参数不是预期类型时或调用对象不存在的方法时发生的错误

  • URIError对象

URI 相关函数的参数不正确时抛出的错误 现在很少使用

  • EvalError错误

eval函数没有被正确执行时抛出的错误 现在已经不使用,为保证兼容性而保留 以上这6种派生错误,连同原始的Error对象,都是构造函数 开发者可以使用它们,手动生成错误对象的实例 这些构造函数都接受一个参数,代表错误提示信息(即message属性)

(3)自定义错误类型
function UserError(message) {
  this.message = message || '默认信息';
  this.name = 'UserError';
}
UserError.prototype = new Error();
UserError.prototype.constructor = UserError;

// 生成这种自定义类型的错误
new UserError('这是自定义的错误!');
(4)throw 语句

throw语句的作用是手动中断程序执行,抛出一个错误;也可以抛出自定义错误;甚至可以抛出任何值

/* 抛出错误 */
var x = -1;
if (x <= 0) {
  throw new Error('x 必须为正数');
}
// Uncaught Error: x 必须为正数

/* 抛出自定义错误 */
function UserError(message) {
  this.message = message || '默认信息';
  this.name = 'UserError';
}
throw new UserError('出错了!');
// Uncaught UserError {message: "出错了!", name: "UserError"}

// 抛出一个字符串
throw 'Error!';
// Uncaught Error!

/* 抛出任何类型的值 */
// 抛出一个数值
throw 42;
// Uncaught 42

// 抛出一个布尔值
throw true;
// Uncaught true

// 抛出一个对象
throw {
  toString: function () {
    return 'Error!';
  }
};
// Uncaught {toString: ƒ}
(5)try-catch 结构

当try代码块抛出错误时,错误会被catch代码块捕获,之后程序不会中断而是会按照正常流程继续执行下去 catch接受一个参数,表示try代码块抛出的值 catch代码块之中,还可以再抛出错误,甚至使用嵌套的try...catch结构

(6)finally 代码块

try...catch结构允许在最后添加一个finally代码块,表示不管是否出现错误,都必需在最后运行的语句

function f() {
  try {
    console.log(0);
    throw 'bug';
  } catch(e) {
    console.log(1);
    return true; // 这句原本会延迟到 finally 代码块结束再执行
    console.log(2); // 不会运行
  } finally {
    console.log(3);
    return false; // 这句会覆盖掉前面那句 return
    console.log(4); // 不会运行
  }

  console.log(5); // 不会运行
}

var result = f();
// 0
// 1
// 3

result
// false

四、作业及提交方式

1. 提交作业

提交课后作业和笔记可以积累积分,积累积分可以获得奖励 课后作业发我的邮箱[email protected]
发送邮件主题按如下格式前端-姓名-第x周-第x次, 如 前端-陈思远-第1周-第1次 本次课程作业在2月16日之前发送到我的邮箱

2. 作业内容

关于这次作业,我思考了很长时间想应该布置什么,最后决定出一些问题,根据我在开发与学习中遇到的问题而设计,主要关于面向对象概念和一些内建对象的使用

1. 设计一个构造函数,该构造函数能够返回一个满足以下各条件的对象
(1)构造函数名/对象名为Circle,即一个能够表示圆形的对象类型;可接受三个参数,分别代表半径、圆心x、y坐标
	可选:参数默认值分别为1、0、0,当传入参数数量不足时起效
(2)Circle类含有radius属性,表示圆形的半径
(3)Circle类含有x和y属性,表示圆心的坐标
(4)Circle类含有radius属性的存取器方法getRadius()与setRadius(),能够分别设置圆形半径与设置圆形半径的方法
(5)Circle类含有两个方法getPerimeter()与getArea(),能够分别返回圆形的周长与面积
(6)Circle类含有getDistance()方法,接受一个圆形实例,返回该圆形的圆心与原圆形的圆心间的距离
	可选:建议添加异常处理模块,当方法内部检测输入参数不是圆形实例时,抛出异常,并被方法内部try-catch模块捕获,并打印错误信息
 
如果有兴趣的话也可以了解以下如何使用ES6中新增的class关键字来创建对象
也可以根据自己的想法添加相应的方法属性保证需求
2. 已有一个数组,假定该数组数组名为array,请根据以下要求写出相应的函数表达式
有些问题有多个答案,尽量尝试以多种方式解决问题

如:array = [1, 2, 3, 4, 5]
问题:试给出数组array的长度,应返回一个Number值
回答:array.length

(1)array = [243, 244, 245, 246, 247]
问题:请检测数值数组array中是否存在元素245,应返回一个boolean值

(2)array = [243, 244, 245, 246, 247]
问题:请检测数值数组array中是否存在元素245,若存在返回该元素第一次出现的数组下标,应为Number值

(3)array = [{id: 243}, {id: 244}, {id: 245}, {id: 246}, {id: 247}]
问题:请从对象数组array中提取出键id的值,并组成一个新的数组返回

(4)array = [2, 22, 222, 2222, 22222]
问题:请从数值数组array中提取所有小于200的数值,并组成一个新的数组返回,若无元素满足返回空数组

(5)array = [2, 22, 222, 2222, 22222]
问题:请对数值数组array中各项求和,并返回一个Number值

(6)array = [1, 2, 3, 4, 5]
问题:请对数值数组array中各项进行自增操作,返回一个数组
3. 有一个字符串,假定该字符串名为str,请根据以下要求写出相应的函数表达式
有些问题有多个答案,尽量尝试以多种方式解决问题
(1)str='atallatappleant' sonstr='at'
问题:请检测字符串中是否存在给定的子字符串,若存在返回子字符串第一次出现的下标
(2)str='www.baidu.com?id=11111&name=chen'
问题:对以上一个URL根据给定的参数名param提取参数值
说明:参数以?与网址连接,以&与其他参数连接;如以上网址有参数名id参数值11111
参考思路:先分离参数部分与前端网址部分,再分离各参数,最后根据参数键值对结构匹配参数名从而实现提取参数值

3. 作业参考代码

参考答案已上传Gitee和Github仓库,参见相应markdown文件 Gitee链接 Github链接