JavaScript学习笔记

JavaScript学习笔记

一、JavaScript基础

继承这块,借鉴文章链接

1.1 为什么学习 JavaScript?

  1. HTML 定义了网页的内容
  2. CSS 描述了网页的布局
  3. JavaScript 网页的行为

在这里插入图片描述

1.2 JavaScript 显示数据

JavaScript 可以通过不同的方式来输出数据:

  1. 使用 window.alert() 弹出警告框。
  2. 使用 document.write() 方法将内容写到 HTML 文档中。
  3. 使用 innerHTML 写入到 HTML 元素。
  4. 使用 console.log() 写入到浏览器的控制台。
  5. 抛出了一个通用错误,带有一条自定义错误消息。浏览器会像处理自己生成的错误一样, 来处理这行代码抛出的错误。换句话说,浏览器会以常规方式报告这一错误,并且会显示这里的自定义错误消息,同时会终止代码执行。,如下:
throw new Error('This is a error message');
console.log('This is the next!');        
try {
      throw new Error('This is a error message');
  } catch (err) {
      console.log(err.message)
  } finally {
      console.log('tom is handsome!');
  }
console.log('This is the next!');
输出
This is a error message
tom is handsome!
This is the next!

1.3 “对象是变量的容器” = “对象是键值对的容器” = 对象是属性和方法的容器。

键值对通常写法为 name : value (键与值以冒号分割)。
键值对在对象通常称为 对象属性

var car = {
	name:'baoma',
	color: 'white',
	drive: function(){
		document.write('车跑起来了')
	}
}
console.log(car.drive)
console.log(car.drive())

在这里插入图片描述

1.4 非严格模式下给未声明变量赋值创建的全局变量,是全局对象的可配置属性,可以删除。

var var1 = 1; // 不可配置全局属性
var2 = 2; // 没有使用 var 声明,可配置全局属性

console.log(this.var1); // 1
console.log(window.var1); // 1
console.log(window.var2); // 2

delete var1; // false 无法删除
console.log(var1); //1

delete var2; 
console.log(delete var2); // true
console.log(var2); // 已经删除 报错变量未定义

1.5 作用域为可访问变量,对象,函数的集合。

(一)局部作用域

  • 变量在函数内声明,变量为局部作用域
  • 局部变量:只能在函数内部访问。
  • 因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。
  • 局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。
// 此处不能调用 carName 变量
function myFunction() {
    var carName = "Volvo";
    // 函数内可调用 carName 变量
}

(二)全局变量

  • 变量在函数外定义,即为全局变量。
  • 全局变量有 全局作用域: 网页中所有脚本和函数均可使用。
var carName = " Volvo";
// 此处可调用 carName 变量
function myFunction() {
    // 函数内可调用 carName 变量
}

(三)如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。

// 此处可调用 carName 变量
function myFunction() {
    carName = "Volvo";
    // 此处可调用 carName 变量
}

(四)变量生命周期

  • 变量生命周期在它声明时初始化。
  • 局部变量在函数执行完毕后销毁。
  • 全局变量在页面关闭后销毁。

(五)在 HTML 中, 全局变量是 window 对象: 所有数据变量都属于 window 对象。

function myFunction() {
    carName = "Volvo";
}
myFunction();
document.write(window.carName);

1.7 在字符串中可以使用转义字符转义的特殊字符:

代码输出
单引号
"双引号
\反斜杠
\n换行
\r回车
\ttab(制表符)
\b退格符
\f换页符

1.8 运算符

  • 运算符 = 用于给 JavaScript 变量赋值。
  • 运算符=== 为绝对相等,即数据类型与值都必须相等。
5==="5"	false
5==5true
  • 自增
let x = 2
y = x++
console.log(x,y)
//打印3,2
x = 2
z = ++x
console.log(x,z)
//打印3,3
  • 三值运算
voteable=(age<18)?"年龄太小":"年龄已达到";

1.9 break 和 continue 语句

for (i=0;i<10;i++)
{
    if (i==3)
    {
        break;
    }
    x=x + "The number is " + i + "<br>";
}

//输出0,1,2
for (i=0;i<10;i++)
{
    if (i==3) continue;
    x=x + "The number is " + i + "<br>";
}
//输出0,1,2,4,5,6,7,8,9

1.10 typeof, null, 和 undefined

typeof "John"                // 返回 string
typeof 3.14                  // 返回 number
typeof false                 // 返回 boolean
typeof [1,2,3,4]             // 返回 object
typeof {name:'John', age:34} // 返回 object
var person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};
var person = undefined;
console.log(person,typeof person)
//输出 undefined "undefined"
var person = null;
console.log(person,typeof person)
//输出 null "object"

null 和 undefined 的值相等,但类型不等:

typeof undefined             // undefined
typeof null                  // object
null === undefined           // false
null == undefined            // true

1.11 constructor 属性

查看对象是否为数组 (包含字符串 “Array”):

function isArray(myArray) {
    return myArray.constructor.toString().indexOf("Array") > -1;
}

查看对象是否为日期 (包含字符串 “Date”):

function isDate(myDate) {
    return myDate.constructor.toString().indexOf("Date") > -1;
}

1.12 类型转换

  1. 类型强制转换
x=5+5;
y="5"+5;
z="5"-5;
n="Hello"+5;
5 + null    // 返回 5   null 转换为 0
//x,y, 和 z 输出结果为:10,"55",0,"Hello5"
  1. 一元运算符 + 可用于将变量转换为数字
    如果变量不能转换,它仍然会是一个数字,但值为 NaN (不是一个数字)
var y = "5";      // y 是一个字符串
var x = + y;      // x 是一个数字 (5)

var y = "John";   // y 是一个字符串
var x = + y;      // x 是一个数字 (NaN)
  1. 将数字转换为字符串两种方式:String()、toString() 可以转化Number、布尔值、日期等
String(123) 
(123).toString()
toExponential()把对象的值转换为指数计数法。
toFixed()把数字转换为字符串,结果的小数点后有指定位数的数字。
toPrecision()把数字格式化为指定的长度
  1. 数字转换为字符串的方法
toExponential()把对象的值转换为指数计数法。
toFixed()把数字转换为字符串,结果的小数点后有指定位数的数字。
toPrecision()把数字格式化为指定的长度。
  1. 日期转换为字符串的函数
getDate()从 Date 对象返回一个月中的某一天 (1 ~ 31)。
getDay()从 Date 对象返回一周中的某一天 (0 ~ 6)。
getFullYear()从 Date 对象以四位数字返回年份。
getHours()返回 Date 对象的小时 (0 ~ 23)。
getMilliseconds()返回 Date 对象的毫秒(0 ~ 999)。
getMinutes()返回 Date 对象的分钟 (0 ~ 59)。
getMonth()从 Date 对象返回月份 (0 ~ 11)。
getSeconds()返回 Date 对象的秒数 (0 ~ 59)。
getTime()返回 1970 年 1 月 1 日至今的毫秒数。
  1. 字符串转为数字的方法
parseFloat()解析一个字符串,并返回一个浮点数。
parseInt()解析一个字符串,并返回一个整数
  1. 将布尔值转换为数字,全局方法 Number()
Number(false)     // 返回 0
Number(true)      // 返回 1
  1. 将日期转换为数字,全局方法 Number() 、getTime()
d = new Date();
Number(d)          // 返回 1404568027739
d = new Date();
d.getTime()        // 返回 1404568027739

1.13 正则表达式

  1. 使用正则表达式搜索 “Runoob” 字符串,且不区分大小写:
var str = "Visit Runoob!"; 
var n = str.search(/Runoob/i);
输出结果为:
6
  1. 使用正则表达式且不区分大小写将字符串中的 Microsoft 替换为 Runoob :
var str = "Visit Microsoft!"; 
var txt = str.replace(/microsoft/i,"Runoob");
结果输出为:
Visit Runoob!
var patt = /e/;
patt.test("The best things in life are free!");
字符串中含有 "e",所以该实例输出为:
true

1.14 JavaScript 变量提升

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。
JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明

x = 5; // 变量 x 设置为 5
elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x;                     // 在元素中显示 x
var x; // 声明 x

1.15 JavaScript 严格模式(use strict)

"use strict";
myFunction();
function myFunction() {
    y = 3.14;   // 报错 (y 未定义)
}
x = 3.14;       // 不报错
myFunction();
function myFunction() {
   "use strict";
    y = 3.14;   // 报错 (y 未定义)
}

为什么使用严格模式:消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;

  • 消除代码运行的一些不安全之处,保证代码运行的安全;
  • 提高编译器效率,增加运行速度;
  • 为未来新版本的Javascript做好铺垫

1.16 HTML5 表单属性

1.17 JavaScript 表单验证

1.18 JavaScript 表单验证 API

1.19 CSS 伪类(Pseudo-classes)

1.20 this 关键字

1.21 var let 和 const

1.22 JSON

  1. JSON 是用于存储和传输数据的格式。
  2. JSON 通常用于服务端向网页传递数据
  3. JSON 英文全称 JavaScript Object Notation
  4. JSON 是一种轻量级的数据交换格式。
函数描述
JSON.parse()用于将一个 JSON 字符串转换为 JavaScript 对象。
JSON.stringify()用于将 JavaScript 值转换为 JSON 字符串。

1.23 javascript:void(0) 含义

  • 一般用于超链接<a>标签的跳转的界面方式

1.24 异步编程

(一)异步的概念

  • 异步(Asynchronous, async)是与同步(Synchronous, sync)相对的概念。
  • 一个异步过程的执行将不再与原有的序列有顺序关系。
  • 同步按你的代码顺序执行,异步不按照代码顺序执行,异步的执行效率更高。
  • 通俗讲:异步就是从主线程发射一个子线程来完成任务。
    在这里插入图片描述

(二)什么时候用异步编程

  • 在前端编程中(甚至后端有时也是这样),我们在处理一些简短、快速的操作时,例如计算 1 + 1 的结果,往往在主线程中就可以完成。主线程作为一个线程,不能够同时接受多方面的请求。所以,当一个事件没有结束时,界面将无法处理其他请求。
  • 现在有一个按钮,如果我们设置它的 onclick 事件为一个死循环,那么当这个按钮按下,整个网页将失去响应。为了避免这种情况的发生,我们常常用子线程来完成一些可能消耗时间足够长以至于被用户察觉的事情,比如读取一个大文件或者发出一个网络请求。因为子线程独立于主线程,所以即使出现阻塞也不会影响主线程的运行。但是子线程有一个局限:一旦发射了以后就会与主线程失去同步。为了解决这个问题,JavaScript 中的异步操作函数往往通过回调函数来实现异步任务的结果处理。

(三)回调函数

  • 回调函数就是一个函数,它是在我们启动一个异步任务的时候就告诉它:等你完成了这个任务之后要干什么。这样一来主线程几乎不用关心异步任务的状态了,他自己会善始善终。

  • setTimeout 会在子线程中等待 3 秒,在 setTimeout 函数执行之后主线程并没有停止,所以:

setTimeout(function () {
    console.log("1");
}, 1000);
console.log("2");
这段程序的执行结果是:2       1

(四)Promise函数

new Promise(function (resolve, reject) {
    console.log(1111);
    resolve(2222);
}).then(function (value) {
    console.log(value);
    return 3333;
}).then(function (value) {
    console.log(value);
    throw "An error";
}).catch(function (err) {
    console.log(err);
});
//执行结果:
1111
2222
3333
An error
  1. resolve() 中可以放置一个参数用于向下一个 then 传递一个值,then 中的函数也可以返回一个值传递给 then。但是,如果 then 中返回的是一个 Promise 对象,那么下一个 then 将相当于对这个返回的 Promise 进行操作
  2. reject() 参数中一般会传递一个异常给之后的 catch 函数用于处理异常。
  3. 但是请注意以下两点:
    • resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列;
    • resolve 和 reject 并不能够使起始函数停止运行,别忘了 return。

(五)Promise 函数–示例
有些 “计时器” 程序看上去比函数瀑布还要长,所以我们可以将它的核心部分写成一个 Promise 函数:

function print(delay, message) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(message);
            resolve();
        }, delay);
    });
}
print(1000, "First").then(function () {
    return print(4000, "Second");
}).then(function () {
    print(3000, "Third");
});
async function asyncFunc() {
    await print(1000, "First");
    await print(4000, "Second");
    await print(3000, "Third");
}
asyncFunc();
//运行结果
First
Second
Third

这种返回值为一个 Promise 对象的函数称作 Promise 函数,它常常用于开发基于异步操作的库。

  • 可以有多个 then块,最好只安排一个 catch 和 finally 块,catch 块只会执行第一个,除非 catch 块里有异常
  • then 块默认会向下顺序执行,return 是不能中断的,可以通过 throw 来跳转至 catch 实现中断
  • 当你又需要调用一个异步任务的时候,就需要再写一个 then 而不是在当前的 then 接着编程

1.25 代码规范

二、JS操作HTML DOM (文档对象模型)

2.1 简介HTML DOM

当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。

HTML DOM 模型被构造为对象的树(即HTML DOM 树)
在这里插入图片描述

2.2 查找 HTML 元素

(一)通过 id 找到 HTML 元素

var x=document.getElementById("intro");

(二)通过标签名找到 HTML 元素

var x=document.getElementById("main");
var y=x.getElementsByTagName("p");

(三)通过类名找到 HTML 元素

var x=document.getElementsByClassName("intro");

2.3 HTML DOM - 改变 HTML

(一)改变 HTML 输出流

document.write(Date());

(二)改变 HTML 内容

document.getElementById(id).innerHTML=新的 HTML

(三)改变 HTML 属性

document.getElementById(id).attribute=新属性值

<!DOCTYPE html>
<html>
<body>
<img id="image" src="smiley.gif">
<script>
document.getElementById("image").src="landscape.jpg";
</script>
</body>
</html>

2.4 HTML DOM - 改变CSS

改变 HTML 样式

//document.getElementById(id).style.property=新样式

document.getElementById("p2").style.color="blue";
document.getElementById("p2").style.fontFamily="Arial";

2.5 HTML DOM 事件

(一)如需在用户点击某个元素时执行代码,请向一个 HTML 事件属性添加 JavaScript 代码:

//οnclick=JavaScript;

<h1 onclick="this.innerHTML='Ooops!'">点击文本!</h1>

(二)使用 HTML DOM 来分配事件

//向 button 元素分配 onclick 事件:

<script>
document.getElementById("myBtn").onclick=function(){displayDate()};
</script>

(三)onload 和 onunload 事件

  • onload 和 onunload 事件会在用户进入或离开页面时被触发。
  • onload 事件可用于检测访问者的浏览器类型和浏览器版本,并基于这些信息来加载网页的正确版本。
  • onload 和 onunload 事件可用于处理 cookie。
//实例
<body onload="checkCookies()">

(四)onchange 事件

  • onchange 事件常结合对输入字段的验证来使用。
  • 如下,当用户改变输入字段的内容时,会调用 upperCase() 函数。
//实例
<input type="text" id="fname" onchange="upperCase()">

(五)onmouseover 和 onmouseout 事件

  • onmouseover 和 onmouseout 事件可用于在用户的鼠标移至 HTML 元素上方或移出元素时触发函数。

(六)onmousedown、onmouseup 以及 onclick 事件

  • onmousedown, onmouseup 以及 onclick 构成了鼠标点击事件的所有部分。首先当点击鼠标按钮时,会触发 onmousedown 事件,当释放鼠标按钮时,会触发 onmouseup 事件,最后,当完成鼠标点击时,会触发 onclick 事件。

(七)onfocus事件

<input type="text" onfocus="myFunction(this)">
<p>当输入框获取焦点时,修改背景色(background-color属性) 将被触发。</p>

2.6 HTML DOM EventListener

(一)addEventListener() 方法

document.getElementById("myBtn").addEventListener("click", displayDate);
function displayDate() {
    document.getElementById("demo").innerHTML = Date();
}

语法:element.addEventListener(event, function, useCapture);

  • 第一个参数是事件的类型 (如 “click” 或 “mousedown”).
  • 第二个参数是事件触发后调用的函数。
  • 第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的。

(二)事件冒泡或事件捕获?

事件传递定义了元素事件触发的顺序。 如果你将 < p > 元素插入到 < div > 元素中,用户点击 < p > 元素, 哪个元素的 "click" 事件先被触发呢?
  • 在 冒泡 中,内部元素的事件会先被触发,然后再触发外部元素,即: < p > 元素的点击事件先触发,然后会触发 < div > 元素的点击事件。
  • 在 捕获 中,外部元素的事件会先被触发,然后才会触发内部元素的事件,即: < div > 元素的点击事件先触发 ,然后再触发 < p > 元素的点击事件。
  • addEventListener() 方法可以指定 “useCapture” 参数来设置传递类型。
  • addEventListener(event, function, useCapture); 默认值为 false, 即冒泡传递,当值为 true 时, 事件使用捕获传递。

实例

document.getElementById("myDiv").addEventListener("click", myFunction, true);

(三)removeEventListener() 方法

removeEventListener() 方法移除由 addEventListener() 方法添加的事件句柄
<div id="myDIV"> div 元素添加了 onmousemove 事件句柄,鼠标在桔红色的框内移动时会显示随机数。
  <p>点击按钮移除 DIV 的事件句柄。</p>
  <button onclick="removeHandler()" id="myBtn">点我</button>
</div>
<p id="demo"></p>
<script>
document.getElementById("myDIV").addEventListener("mousemove", myFunction);
function myFunction() {
    document.getElementById("demo").innerHTML = Math.random();
}
function removeHandler() {
    document.getElementById("myDIV").removeEventListener("mousemove", myFunction);
}
</script>

2.7 HTML DOM 元素 (节点)

(一)创建新的 HTML 元素 (节点) - appendChild(),插入到尾部

要创建新的 HTML 元素 (节点)需要先创建一个元素,然后在已存在的元素中添加它。
<div id="div1">
<p id="p1">这是一个段落。</p>
<p id="p2">这是另外一个段落。</p>
</div>
 
<script>
var para = document.createElement("p");
var node = document.createTextNode("这是一个新的段落。");
para.appendChild(node);
 
var element = document.getElementById("div1");
element.appendChild(para);
</script>

实例解析

//以下代码是用于创建 < p > 元素:
//为 <p> 元素创建一个新的文本节点:
var para = document.createElement("p");
//将文本节点添加到 <p> 元素中:
var node = document.createTextNode("这是一个新的段落。");
//最后,在一个已存在的元素中添加 p 元素内容。
para.appendChild(node);
//查找已存在的元素:
var element = document.getElementById("div1");
//添加到已存在的元素中:
element.appendChild(para);

(二)创建新的 HTML 元素 (节点) - insertBefore(),插入到头部

<div id="div1">
<p id="p1">这是一个段落。</p>
<p id="p2">这是另外一个段落。</p>
</div>
 
<script>
var para = document.createElement("p");
var node = document.createTextNode("这是一个新的段落。");
para.appendChild(node);
 
var element = document.getElementById("div1");
var child = document.getElementById("p1");
element.insertBefore(para, child);
</script>

(三)移除已存在的元素

要移除一个元素,你需要知道该元素的父元素。
<div id="div1">
<p id="p1">这是一个段落。</p>
<p id="p2">这是另外一个段落。</p>
</div>
 
<script>
var parent = document.getElementById("div1");
var child = document.getElementById("p1");
parent.removeChild(child);
</script>

实例解析

//HTML 文档中 <div> 元素包含两个子节点 (两个 <p> 元素):
<div id="div1">
	<p id="p1">这是一个段落。</p>
	<p id="p2">这是另外一个段落。</p>
</div>
//查找 id="div1" 的元素:
var parent = document.getElementById("div1");
//查找 id="p1" 的 <p> 元素:
var child = document.getElementById("p1");
//从父元素中移除子节点:
parent.removeChild(child);
以下代码是已知要查找的子元素,然后查找其父元素,再删除这个子元素(删除节点必须知道父节点)
var child = document.getElementById("p1");
child.parentNode.removeChild(child);

(四)替换 HTML 元素 - replaceChild()

<div id="div1">
<p id="p1">这是一个段落。</p>
<p id="p2">这是另外一个段落。</p>
</div>
 
<script>
var para = document.createElement("p");
var node = document.createTextNode("这是一个新的段落。");
para.appendChild(node);
 
var parent = document.getElementById("div1");
var child = document.getElementById("p1");
parent.replaceChild(para, child);
</script>

2.8 HTMLCollection

  1. HTMLCollection是一个接口,表示 HTML 元素的集合,它提供了可以遍历列表的方法和属性
  2. getElementsByTagName() 方法返回 HTMLCollection 对象。
  3. HTMLCollection 对象类似包含 HTML 元素的一个数组。
//获取 <p> 元素的集合:
var myCollection = document.getElementsByTagName("p");
//显示集合元素个数:
document.getElementById("demo").innerHTML = myCollection.length;
//集合 length 属性常用于遍历集合中的元素。
//修改所有 <p> 元素的背景颜色:
var myCollection = document.getElementsByTagName("p");
var i;
for (i = 0; i < myCollection.length; i++) {
    myCollection[i].style.backgroundColor = "red";
}

HTMLCollection 不是一个数组!
HTMLCollection 看起来可能是一个数组,但其实不是。
你可以像数组一样,使用索引来获取元素。
HTMLCollection 无法使用数组的方法: valueOf(), pop(), push(), 或 join() 。

2.9 HTML DOM 节点列表

NodeList 对象是一个从文档中获取的节点列表 (集合) 。
NodeList 对象类似 HTMLCollection 对象。
所有浏览器的 childNodes 属性返回的是 NodeList 对象。
大部分浏览器的 querySelectorAll() 返回 NodeList 对象。

实例

//以下代码选取了文档中所有的 < p > 节点:
var myNodeList = document.querySelectorAll("p");
//NodeList 中的元素可以通过索引(以 0 为起始位置)来访问。
//访问第二个 <p> 元素可以是以下代码:
y = myNodeList[1];

NodeList 对象 length 属性
NodeList 对象 length 属性定义了节点列表中元素的数量。

示例

var myNodelist = document.querySelectorAll("p");
document.getElementById("demo").innerHTML = myNodelist.length;

HTMLCollection 与 NodeList 的区别与联系

  • HTMLCollection 是 HTML 元素的集合。NodeList 是一个文档节点的集合。
  • NodeList 与 HTMLCollection 都与数组对象有点类似,可以使用索引 (0, 1, 2, 3, 4, …) 来获取元素。
  • NodeList 与 HTMLCollection 都有 length 属性。
  • HTMLCollection 元素可以通过 name,id 或索引来获取。NodeList 只能通过索引来获取。
  • 只有 NodeList 对象有包含属性节点和文本节点。
  • NodeList(节点列表)看起来可能是一个数组,但其实不是一个数组!无法使用数组的方法: valueOf(), pop(), push(), 或 join() 。

三、JavaScript对象

for…in 语句循环遍历对象的属性

var person={fname:"Bill",lname:"Gates",age:56}; 
for (x in person){
	console.log(x)   // 结果为fname,lname,age
	console.log(person[x])   // 结果为Bill,Gates,56
}

String 对象

Number对象

Date对象

Array数组

Object字典 对象

  • Object.assign()
    将所有可枚举的自身属性的值从一个或多个源对象复制到目标对象。
  • Object.entries()
    返回一个包含[key, value]给定对象自己的可枚举字符串属性的所有对的数组。
  • Object.freeze()
    冻结对象。其他代码无法删除或更改其属性。
  • Object.fromEntries()
    从可迭代的[key, value]对中返回一个新对象。(这是的反向 Object.entries)。
  • Object.keys()
    返回一个数组,其中包含给定对象自己的所有可枚举字符串属性的名称。
  • Object.values()
    返回一个数组,该数组包含与给定对象自己的所有可枚举字符串属性相对应的值。

Boolean 对象

Math 对象

RegExp 对象

全局属性/函数

运算符

Error(错误) 对象

更多详见

四、Browser 对象

Window 对象
Navigator 对象
Screen 对象
History 对象
Location 对象
存储 对象
弹窗
计时事件
Cookie

五、DOM 对象

六、HTML 对象(了解)

七、js继承的6种方式

想要继承,就必须要提供个父类(继承谁,提供继承的属性)
//父类
function Person(name){ //给构造函数添加参数
    this.name = name;
    this.call = function(){
        console.log(this.name + ' is calling')
    }
}
//给构造函数添加了原型属性、方法(函数)
Person.prototype.age = 10;
Person.prototype.call1 = function(){
    console.log(this.name + ' is calling1')
}
console.log(new Person('hzy1'))
console.log(new Person('hzy2').call1())
//结果如下图

tupian

一、原型链继承

//原型链继承
function Person1() {
    this.name = 'hzy1';
}

Person1.prototype = new Person(); //主要
var person1 = new Person1();
console.log(person1.age); // 10
console.log(person1.name); //hzy1
//instanceof 判断元素是否在另一个元素的原型链上
//若person1继承了Person的属性,返回true
console.log(person1 instanceof Person); // true

在这里插入图片描述

重点:让新实例的原型等于父类的实例。
    特点:1、实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(新实例不会继承父类实例的属性!)
    缺点:1、新实例无法向父类构造函数传参。
       2、继承单一。
       3、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)

二、借用构造函数继承

 //父类
function Person(name){
    this.name = name;
    this.call = function(){
        console.log(this.name + ' is calling')
    }
}

Person.prototype.age = 10;
Person.prototype.call1 = function(){
    console.log(this.name + ' is calling1')
}
//借助构造函数继承
function Person2(){
    Person.call(this,'hzy'); //重点
    this.age = 12;
}

var person2 = new Person2();
console.log(person2);
console.log(person2.name); //"hzy"
console.log(person2.age); // 12
console.log(person2 instanceof Person); // false

在这里插入图片描述

重点:用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))
    特点:1、只继承了父类构造函数的属性,没有继承父类原型的属性。
       2、解决了原型链继承缺点1、2、3。
       3、可以继承多个构造函数属性(call多个)。
       4、在子实例中可向父实例传参。
    缺点:1、只能继承父类构造函数的属性。
       2、无法实现构造函数的复用。(每次用每次都要重新调用)
       3、每个新实例都有父类构造函数的副本,臃肿。

三、组合继承(组合原型链继承和借用构造函数继承)(常用)

 //父类
function Person(name){
    this.name = name;
    this.call = function(){
        console.log(this.name + ' is calling')
    }
}

Person.prototype.age = 10;
Person.prototype.call1 = function(){
    console.log(this.name + ' is calling1')
}
//组合原型链和构造函数继承
function Person3(name){
    Person.call(this,name); //借用构造函数模式
}
Person3.prototype = new Person()//原型链继承
var person3 = new Person3('hzy');
console.log(person3)
console.log(person3.name); //“hzy”继承了构造函数属性
console.log(person3.age); //10继承了父类原型的属性

在这里插入图片描述

重点:结合了两种模式的优点,传参和复用
    特点:1、可以继承父类原型上的属性,可以传参,可复用。
       2、每个新实例引入的构造函数属性是私有的。
    缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。

四、原型式继承

 //父类
function Person(name){
    this.name = name;
    this.call = function(){
        console.log(this.name + ' is calling')
    }
}

Person.prototype.age = 10;
Person.prototype.call1 = function(){
    console.log(this.name + ' is calling1')
}
//先封装一个函数容器,用来输出对象和承载继承的原型
function content(obj){
    function F(){}
    F.prototype = obj;//继承了传入的参数
    return new F() //返回函数对象
}
var Person4 = new Person(); //拿到父类的实例
var person4 = content(Person4);
console.log(person4);
console.log(person4.age); //10  继承了父类函数的属性

在这里插入图片描述

重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。
    特点:类似于复制一个对象,用函数来包装。
    缺点:1、所有实例都会继承原型上的属性。
       2、无法实现复用。(新实例属性都是后面添加的)

五、寄生式继承

 //父类
function Person(name){
    this.name = name;
    this.call = function(){
        console.log(this.name + ' is calling')
    }
}

Person.prototype.age = 10;
Person.prototype.call1 = function(){
    console.log(this.name + ' is calling1')
}
//先封装一个函数容器,用来输出对象和承载继承的原型
function content(obj){
    function F(){}
    F.prototype = obj;//继承了传入的参数
    return new F() //返回函数对象
}
var person = new Person(); //拿到父类的实例
//以上是原型式继承,给原型式继承再套个壳子传递参数
function Person6(obj){
    var temp = content(obj);
    temp.name = "hzy";
    return temp;
}
var person6 = Person6(person)
//这个函数经过声明之后就成了可增添属性的对象
console.log(person6)
console.log(typeof Person6); //function
console.log(typeof person6); //object
console.log(person6.name); //"hzy",  返回1了个temp对象,继承了temp的属性

在这里插入图片描述

重点:就是给原型式继承外面套了个壳子。
    优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。
    缺点:没用到原型,无法复用。

六、寄生组合式继承(常用)
  寄生:在函数内返回对象然后调用
  组合:1、函数的原型等于另一个实例。2、在函数中用apply或者call引入另一个构造函数,可传参。

 //父类
function Person(name){
    this.name = name;
    this.call = function(){
        console.log(this.name + ' is calling')
    }
}

Person.prototype.age = 10;
Person.prototype.call1 = function(){
    console.log(this.name + ' is calling1')
}
//先封装一个函数容器,用来输出对象和承载继承的原型
function content(obj){
    function F(){}
    F.prototype = obj;//继承了传入的参数
    return new F() //返回函数对象
}
//content就是F实例的另一种表示法
var con = content(Person.prototype);
//con实例(F实例)的原型继承了父类函数的原型
//上述更像是原型链继承,只不过只继承了原型属性

//组合
function Sub(){
    Person.call(this);//这个继承了父类构造函数的属性
}//解决了组合式俩次调用构造函数属性的缺点
//重点
Sub.prototype = con;//继承了con实例
con.constructor = Sub; //一定要修复实例
var sub1 = new Sub();
//Sub的实例就继承了构造函数属性,父类实例,con的函数属性
console.log(sub1);
console.log(sub1.age);

在这里插入图片描述

重点:修复了组合继承的问题

继承这些知识点与其说是对象的继承,更像是函数的功能用法,如何用函数做到复用,组合,这些和使用继承的思考是一样的。上述几个继承的方法都可以手动修复他们的缺点,但就是多了这个手动修复就变成了另一种继承模式。
    这些继承模式的学习重点是学它们的思想,不然你会在coding书本上的例子的时候,会觉得明明可以直接继承为什么还要搞这么麻烦。就像原型式继承它用函数复制了内部对象的一个副本,这样不仅可以继承内部对象的属性,还能把函数(对象,来源内部对象的返回)随意调用,给它们添加属性,改个参数就可以改变原型对象,而这些新增的属性也不会相互影响。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值