前端基礎知識整理匯總(上)

HTML頁面的生命周期有以下三個重要事件:
DOMContentLoaded —— 瀏覽器已經(jīng)完全加載了 HTML,DOM 樹已經(jīng)構建完畢,但是像是?
// 帶有src屬性的元素不應該在標簽之間包含額外的js代碼,即使包含,只會下載并執(zhí)行外部文件,內(nèi)部代碼也會被忽略。與嵌入式js代碼一樣, 在解析外部js文件時,頁面的處理會暫時停止。
改變腳本行為的方法
1. defer: 立即下載,延遲執(zhí)行
加載和渲染后續(xù)文檔元素的過程將和腳本的加載并行進行(異步),但是腳本的執(zhí)行會在所有元素解析完成之后。腳本總會按照聲明順序執(zhí)行。
在DOMContentLoaded事件之前執(zhí)行。2. async: 異步腳本
加載和渲染后續(xù)文檔元素的過程將和腳本的加載與執(zhí)行并行進行(異步)。但是
async?在下載完畢后的執(zhí)行會阻塞HTML的解析。腳本加載后馬上執(zhí)行,不能保證異步腳本按照他們在頁面中出現(xiàn)的順序執(zhí)行。
一定會在load事件之前執(zhí)行,可能會在DOMContentLoaded事件之前或之后執(zhí)行。區(qū)別:

meta META標簽是HTML標記HEAD區(qū)的一個關鍵標簽,它提供的信息雖然用戶不可見,但卻是文檔的最基本的元信息。除了提供文檔字符集、使用語言、作者等網(wǎng)頁相關信息外,還可以設置信息給搜索引擎,目的是為了SEO(搜索引擎優(yōu)化)。
HTML 元素表示那些不能由其它 HTML 元相關(meta-related)元素((
、, 后臺代碼:
$callback = $_GET['callback'];//得到回調(diào)函數(shù)名$data = array('a','b','c');//要返回的數(shù)據(jù)echo $callback.'('.json_encode($data).')';//輸出?>CORS - 跨域資源共享
CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。
CORS有兩種請求,簡單請求和非簡單請求。只要同時滿足以下兩大條件,就屬于簡單請求。
請求方法是以下三種方法之一:HEAD,GET,POST
HTTP的頭信息不超出以下幾種字段:Accept,Accept-Language,Content-Language,Last-Event-ID,Content-Type【只限于三個值application/x-www-form-urlencoded、multipart/form-data、text/plain】,沒有自定義的HTTP頭部。
簡單請求
瀏覽器:把客戶端腳本所在的域填充到Origin header里,向其他域的服務器請求資源。
服務器:根據(jù)資源權限配置,在響應頭中添加Access-Control-Allow-Origin Header,返回結果。
瀏覽器:比較服務器返回的Access-Control-Allow-Origin Header和請求域的Origin。如果當前域已經(jīng)得到授權,則將結果返回給頁面。否則瀏覽器忽略此次響應。
網(wǎng)頁:收到返回結果或者瀏覽器的錯誤提示。
對于簡單的跨域請求,只要服務器設置的
Access-Control-Allow-Origin?Header和請求來源匹配,瀏覽器就允許跨域。服務器端設置的`Access-Control-Allow-Methods和Access-Control-Allow-Headers對簡單跨域沒有作用。非簡單請求
瀏覽器:先向服務器發(fā)送一個OPTIONS預檢請求,檢測服務器端是否支持真實請求進行跨域資源訪問,瀏覽器會在發(fā)送OPTIONS請求時會自動添加Origin Header 、Access-Control-Request-Method Header和Access-Control-Request-Headers Header。
服務器:響應OPTIONS請求,會在responseHead里添加
Access-Control-Allow-Methods?head。這其中的method的值是服務器給的默認值,可能不同的服務器添加的值不一樣。服務器還會添加Access-Control-Allow-Origin?Header和Access-Control-Allow-Headers?Header。這些取決于服務器對OPTIONS請求具體如何做出響應。如果服務器對OPTIONS響應不合你的要求,你可以手動在服務器配置OPTIONS響應,以應對帶預檢的跨域請求。在配置服務器OPTIONS的響應時,可以添加Access-Control-Max-Age head告訴瀏覽器在一定時間內(nèi)無需再次發(fā)送預檢請求,但是如果瀏覽器禁用緩存則無效。瀏覽器:接到OPTIONS的響應,比較真實請求的method是否屬于返回的
Access-Control-Allow-Methods?head的值之一,還有origin, head也會進行比較是否匹配。如果通過,瀏覽器就繼續(xù)向服務器發(fā)送真實請求, 否則就會報預檢錯誤:請求來源不被options響應允許,請求方法不被options響應允許或請求中有自定義header不被options響應允許。服務器:響應真實請求,在響應頭中放入
Access-Control-Allow-Origin?Header、Access-Control-Allow-Methods和Access-Control-Allow-Headers?Header,分別表示允許跨域資源請求的域、請求方法和請求頭,并返回數(shù)據(jù)。瀏覽器:接受服務器對真實請求的返回結果,返回給網(wǎng)頁
網(wǎng)頁:收到返回結果或者瀏覽器的錯誤提示。
Access-Control-Allow-Origin在響應options請求和響應真實請求時都是有作用的,兩者必須同時包含要跨域的源。?Access-Control-Allow-Methods和Access-Control-Allow-Headers只在響應options請求時有作用。攜帶cookie
在 CORS 跨域中,瀏覽器并不會自動發(fā)送 Cookie。對于普通跨域請求只需服務端設置,而帶cookie跨域請求前后端都需要設置。
瀏覽器,對于跨域請求,需要設置
withCredentials?屬性為 true。服務端的響應中必須攜帶?Access-Control-Allow-Credentials: true?。除了
Access-Control-Allow-Credentials之外,跨域發(fā)送 Cookie 還要求?Access-Control-Allow-Origin不允許使用通配符。否則瀏覽器將會拋出The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'?錯誤。事實上不僅不允許通配符,而且只能指定單一域名。計算 Access-Control-Allow-Origin
既然
Access-Control-Allow-Origin只允許單一域名, 服務器可能需要維護一個接受 Cookie 的 Origin 列表, 驗證?Origin?請求頭字段后直接將其設置為Access-Control-Allow-Origin的值。在 CORS 請求被重定向后?Origin?頭字段會被置為?null, 此時可以選擇從Referer頭字段計算得到Origin。具體實現(xiàn)
服務器端的響應頭配置
Access-Control-Allow-Origin 可以設置為*?,表示可以與任意域進行數(shù)據(jù)共享。// 設置服務器接受跨域的域名"Access-Control-Allow-Origin": "http://127.0.0.1:8080",// 設置服務器接受跨域的請求方法'Access-Control-Allow-Methods': 'OPTIONS,HEAD,DELETE,GET,PUT,POST',// 設置服務器接受跨域的headers'Access-Control-Allow-Headers': 'x-requested-with, accept, origin, content-type',// 設置服務器不用再次預檢請求時間'Access-Control-Max-Age': 10000,// 設置服務器接受跨域發(fā)送Cookie'Access-Control-Allow-Credentials': truedocument.domain
此方案僅限主域相同,子域不同的跨域應用場景。
實現(xiàn)原理:兩個頁面都通過js強制設置document.domain為基礎主域,就實現(xiàn)了同域。
栗子:
在父頁面?http://xxx.com/a.html?中設置document.domain
在子頁面http://xxx.com/b.html?中設置document.domain
window.postMessage
window.postMessage是html5的功能,是客戶端和客戶端直接的數(shù)據(jù)傳遞,既可以跨域傳遞,也可以同域傳遞。
postMessage(data, origin)方法接受兩個參數(shù):
data:html5規(guī)范支持任意基本類型或可復制的對象,但部分瀏覽器只支持字符串,所以傳參時最好用JSON.stringify()序列化。
origin:協(xié)議+主機+端口號,也可以設置為"*",表示可以傳遞給任意窗口,如果要指定和當前窗口同源的話設置為"/"。
栗子:
假如有一個頁面,頁面中拿到部分用戶信息,點擊進入另外一個頁面,另外的頁面默認是取不到用戶信息的,你可以通過window.postMessage把部分用戶信息傳到這個頁面中。(需要考慮安全性等方面。)發(fā)送消息:
// 彈出一個新窗口var domain = 'http://haorooms.com';var myPopup = window.open(`${domain}/windowPostMessageListener.html`,'myWindow');// 發(fā)送消息setTimeout(function(){var message = {name:"站點",sex:"男"};console.log('傳遞的數(shù)據(jù)是 ' + message);myPopup.postMessage(message, domain);}, 1000);接收消息:
// 監(jiān)聽消息反饋window.addEventListener('message', function(event) {// 判斷域名是否正確if (event.origin !== 'http://haorooms.com') return;console.log('received response: ', event.data);}, false);如下圖,接受頁面得到數(shù)據(jù)

如果是使用iframe,代碼應該這樣寫:
// 捕獲iframevar domain = 'http://haorooms.com';var iframe = document.getElementById('myIFrame').contentWindow;// 發(fā)送消息setTimeout(function(){var message = {name:"站點",sex:"男"};console.log('傳遞的數(shù)據(jù)是: ' + message);iframe.postMessage(message, domain);},1000);接收數(shù)據(jù)并反饋信息:
// 響應事件window.addEventListener('message',function(event) {if(event.origin !== 'http://haorooms.com') return;console.log('message received: ' + event.data, event);event.source.postMessage(event.origin);}, false);幾個比較重要的事件屬性:
source – 消息源,消息的發(fā)送窗口/iframe。
origin – 消息源的URI(可能包含協(xié)議、域名和端口),用來驗證數(shù)據(jù)源。
data – 發(fā)送方發(fā)送給接收方的數(shù)據(jù)。window.name
原理:
window對象有個name屬性,該屬性有個特征:即在一個窗口(window)的生命周期內(nèi),窗口載入的所有的頁面都是共享一個window.name,每個頁面對window.name都有讀寫的權限,window.name是持久存在一個窗口載入過的所有頁面中的。栗子:
在子頁面(b.com/data.html) 設置window.name:/* b.com/data.html */在父頁面(a.com/app.html)中創(chuàng)建一個iframe,把其src指向子頁面。在父頁面監(jiān)聽iframe的onload事件,獲取子頁面數(shù)據(jù):
/* a.com/app.html */http-proxy-middleware
http-proxy-middleware用于把請求代理轉(zhuǎn)發(fā)到其他服務器的中間件。
安裝:npm install http-proxy-middleware --save-dev
配置如下:module.exports = {devServer: {contentBase: path.resolve(__dirname, 'dev'),publicPath: '/',historyApiFallback: true,proxy: {// 請求到 '/device' 下的請求都會被代理到target:http://target.com中'/device/*': {target: 'http://target.com',secure: false, // 接受運行在https上的服務changeOrigin: true}}}}使用如下:
fetch('/device/space').then(res => {// 被代理到 http://target.com/device/spacereturn res.json();});// 使用的url 必須以/開始 否則不會代理到指定地址fetch('device/space').then(res => {// http://localhost:8080/device/space 訪問本地服務return res.json();});nginx反向代理
反向代理(Reverse Proxy)方式是指以代理服務器來接受客戶端的連接請求,然后將請求轉(zhuǎn)發(fā)給內(nèi)部網(wǎng)絡上的服務器;并將從服務器上得到的結果返回給客戶端,此時代理服務器對外就表現(xiàn)為一個服務器。
反向代理服務器對于客戶端而言它就像是原始服務器,并且客戶端不需要進行任何特別的設置。客戶端向反向代理 的命名空間(name-space)中的內(nèi)容發(fā)送普通請求,接著反向代理將判斷向何處(原始服務器)轉(zhuǎn)交請求,并將獲得的內(nèi)容返回給客戶端,就像這些內(nèi)容 原本就是它自己的一樣。
模塊化 AMD/CMD/CommonJs都是JS模塊化開發(fā)的標準,目前對應的實現(xiàn)是RequireJS,SeaJs, nodeJs;
CommonJS:服務端js
CommonJS 是以在瀏覽器環(huán)境之外構建 javaScript 生態(tài)系統(tǒng)為目標而產(chǎn)生的寫一套規(guī)范,主要是為了解決 javaScript 的作用域問題而定義的模塊形式,可以使每個模塊它自身的命名空間中執(zhí)行。
實現(xiàn)方法:模塊必須通過 module.exports 導出對外的變量或者接口,通過 require() 來導入其他模塊的輸出到當前模塊的作用域中;
主要針對服務端(同步加載文件)和桌面環(huán)境中,node.js 遵循的是 CommonJS 的規(guī)范;CommonJS 加載模塊是同步的,所以只有加載完成才能執(zhí)行后面的操作。
require()用來引入外部模塊;
exports對象用于導出當前模塊的方法或變量,唯一的導出口;
module對象就代表模塊本身。
// 定義一個module.js文件var A = () => console.log('我是定義的模塊');// 1.第一種返回方式module.exports = A;// 2.第二種返回方式module.exports.test = A// 3.第三種返回方式exports.test = A;// 定義一個test.js文件【這兩個文件在同一個目錄下】var module = require("./module");//調(diào)用這個模塊,不同的返回方式用不同的方式調(diào)用// 1.第一種調(diào)用方式module();// 2.第二種調(diào)用方式module.test();// 3.第三種調(diào)用方式module.test();// 執(zhí)行文件node test.jsAMD:異步模塊定義【瀏覽器端js】
AMD 是 Asynchronous Module Definition 的縮寫,意思是異步模塊定義;采用的是異步的方式進行模塊的加載,在加載模塊的時候不影響后邊語句的運行。主要是為前端 js 的表現(xiàn)指定的一套規(guī)范。
實現(xiàn)方法:通過define方法去定義模塊,通過require方法去加載模塊。
define(id?,dependencies?,factory): 它要在聲明模塊的時候制定所有的依賴(dep),并且還要當做形參傳到factory中。沒什么依賴,就定義簡單的模塊(或者叫獨立的模塊)require([modules], callback): 第一個參數(shù)[modules],是需加載的模塊名數(shù)組;第二個參數(shù)callback,是模塊加載成功之后的回調(diào)函數(shù)主要針對瀏覽器js,requireJs遵循的是 AMD 的規(guī)范;
// module1.js文件, 定義獨立的模塊define({methodA: () => console.log('我是module1的methodA');methodB: () => console.log('我是module1的methodB');});// module2.js文件, 另一種定義獨立模塊的方式define(() => {return {methodA: () => console.log('我是module2的methodA');methodB: () => console.log('我是module2的methodB');};});// module3.js文件, 定義非獨立的模塊(這個模塊依賴其他模塊)define(['module1', 'module2'], (m1, m2) => {return {methodC: () => {m1.methodA();m2.methodB();}};});//定義一個main.js,去加載這些個模塊require(['module3'], (m3) => {m3.methodC();});// 為避免造成網(wǎng)頁失去響應,解決辦法有兩個,一個是把它放在網(wǎng)頁底部加載,另一個是寫成下面這樣:// async屬性表明這個文件需要異步加載,避免網(wǎng)頁失去響應。// IE不支持這個屬性,只支持defer,所以把defer也寫上。// data-main屬性: 指定網(wǎng)頁程序的主模塊// 控制臺輸出結果我是module1的methodA我是module2的methodBCMD:通用模塊定義【瀏覽器端js】
CMD 是 Common Module Definition 的縮寫,通過異步的方式進行模塊的加載的,在加載的時候會把模塊變?yōu)樽址馕鲆槐椴胖酪蕾嚵四膫€模塊;
主要針對瀏覽器端(異步加載文件),按需加載文件。對應的實現(xiàn)是seajs
AMD和CMD的區(qū)別
對于依賴的模塊,AMD 是提前執(zhí)行,CMD 是延遲執(zhí)行。不過 RequireJS 從 2.0 開始,也改成可以延遲執(zhí)行(根據(jù)寫法不同,處理方式不同)。CMD 推崇 as lazy as possible(盡可能的懶加載,也稱為延遲加載,即在需要的時候才加載)。
CMD 推崇依賴就近,AMD 推崇依賴前置。
// CMDdefine(function(require, exports, module) {var a = require('./a');a.doSomething();// ...var b = require('./b'); // 依賴可以就近書寫b.doSomething();// ...})// AMDdefine(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好a.doSomething();// ...b.doSomething();//...})import和require區(qū)別
import和require都是被模塊化使用。
require是CommonJs的語法(AMD規(guī)范引入方式),CommonJs的模塊是對象。import是es6的一個語法標準(瀏覽器不支持,本質(zhì)是使用node中的babel將es6轉(zhuǎn)碼為es5再執(zhí)行,import會被轉(zhuǎn)碼為require),es6模塊不是對象。require是運行時加載整個模塊(即模塊中所有方法),生成一個對象,再從對象上讀取它的方法(只有運行時才能得到這個對象,不能在編譯時做到靜態(tài)化),理論上可以用在代碼的任何地方。import是編譯時調(diào)用,確定模塊的依賴關系,輸入變量(es6模塊不是對象,而是通過export命令指定輸出代碼,再通過import輸入,只加載import中導的方法,其他方法不加載),import具有提升效果,會提升到模塊的頭部(編譯時執(zhí)行)
export和import可以位于模塊中的任何位置,但是必須是在模塊頂層,如果在其他作用域內(nèi),會報錯(es6這樣的設計可以提高編譯器效率,但沒法實現(xiàn)運行時加載)。
require是賦值過程,把require的結果(對象,數(shù)字,函數(shù)等),默認是export的一個對象,賦給某個變量(復制或淺拷貝)。import是解構過程(需要誰,加載誰)。
require/exports:
// require: 真正被require出來的是來自module.exports指向的內(nèi)存塊內(nèi)容const a = require('a') //// exports: 只是 module.exports的引用,輔助module.exports操作內(nèi)存中的數(shù)據(jù)exports.a = amodule.exports = aimport/export:
// importimport a from 'a';import { default as a } from 'a';import * as a from 'a';import { fun1,fun2 } from 'a';// exportexport default a;export const a = 1;export functon a { ... };export { fun1, fun2 };http和https Http:超文本傳輸協(xié)議(Http,HyperText Transfer Protocol)是互聯(lián)網(wǎng)上應用最為廣泛的一種網(wǎng)絡協(xié)議。設計Http最初的目的是為了提供一種發(fā)布和接收HTML頁面的方法。它可以使瀏覽器更加高效。Http協(xié)議是以明文方式發(fā)送信息的,如果黑客截取了Web瀏覽器和服務器之間的傳輸報文,就可以直接獲得其中的信息。
Https:是以安全為目標的Http通道,是Http的安全版。Https的安全基礎是SSL。SSL協(xié)議位于TCP/IP協(xié)議與各種應用層協(xié)議之間,為數(shù)據(jù)通訊提供安全支持。SSL協(xié)議可分為兩層:SSL記錄協(xié)議(SSL Record Protocol),它建立在可靠的傳輸協(xié)議(如TCP)之上,為高層協(xié)議提供數(shù)據(jù)封裝、壓縮、加密等基本功能的支持。SSL握手協(xié)議(SSL Handshake Protocol),它建立在SSL記錄協(xié)議之上,用于在實際的數(shù)據(jù)傳輸開始前,通訊雙方進行身份認證、協(xié)商加密算法、交換加密密鑰等。
HTTP與HTTPS的區(qū)別
1、HTTP是超文本傳輸協(xié)議,信息是明文傳輸,HTTPS是具有安全性的SSL加密傳輸協(xié)議。
2、HTTPS協(xié)議需要ca申請證書,一般免費證書少,因而需要一定費用。
3、HTTP和HTTPS使用的是完全不同的連接方式,用的端口也不一樣。前者是80,后者是443。
4、HTTP連接是無狀態(tài)的,HTTPS協(xié)議是由SSL+HTTP協(xié)議構建的可進行加密傳輸、身份認證的網(wǎng)絡協(xié)議,安全性高于HTTP協(xié)議。https的優(yōu)點
盡管HTTPS并非絕對安全,掌握根證書的機構、掌握加密算法的組織同樣可以進行中間人形式的攻擊,但HTTPS仍是現(xiàn)行架構下最安全的解決方案,主要有以下幾個好處:
1)使用HTTPS協(xié)議可認證用戶和服務器,確保數(shù)據(jù)發(fā)送到正確的客戶機和服務器;
2)HTTPS協(xié)議是由SSL+HTTP協(xié)議構建的可進行加密傳輸、身份認證的網(wǎng)絡協(xié)議,要比http協(xié)議安全,可防止數(shù)據(jù)在傳輸過程中不被竊取、改變,確保數(shù)據(jù)的完整性。
3)HTTPS是現(xiàn)行架構下最安全的解決方案,雖然不是絕對安全,但它大幅增加了中間人攻擊的成本。
4)谷歌曾在2014年8月份調(diào)整搜索引擎算法,并稱“比起同等HTTP網(wǎng)站,采用HTTPS加密的網(wǎng)站在搜索結果中的排名將會更高”。Https的缺點
1)Https協(xié)議握手階段比較費時,會使頁面的加載時間延長近。
2)Https連接緩存不如Http高效,會增加數(shù)據(jù)開銷,甚至已有的安全措施也會因此而受到影響;
3)SSL證書通常需要綁定IP,不能在同一IP上綁定多個域名,IPv4資源不可能支撐這個消耗。
4)Https協(xié)議的加密范圍也比較有限。最關鍵的,SSL證書的信用鏈體系并不安全,特別是在某些國家可以控制CA根證書的情況下,中間人攻擊一樣可行。遍歷方法 for
在for循環(huán)中,循環(huán)取得數(shù)組或是數(shù)組類似對象的值,譬如arguments和HTMLCollection對象。
不足:
在于每次循環(huán)的時候數(shù)組的長度都要去獲取;
終止條件要明確;
foreach(),map()
兩個方法都可以遍歷到數(shù)組的每個元素,而且參數(shù)一致;
forEach(): 對數(shù)組的每個元素執(zhí)行一次提供的函數(shù), 總是返回undefined;
map(): 創(chuàng)建一個新數(shù)組,其結果是該數(shù)組中的每個元素都調(diào)用一個提供的函數(shù)后返回的結果。返回值是一個新的數(shù)組;var array1 = [1,2,3,4,5];var x = array1.forEach((value,index) => {console.log(value);return value + 10;});console.log(x); // undefinedvar y = array1.map((value,index) => {console.log(value);return value + 10;});console.log(y); // [11, 12, 13, 14, 15]for in
經(jīng)常用來迭代對象的屬性或數(shù)組的每個元素,它包含當前屬性的名稱或當前數(shù)組元素的索引。
當遍歷一個對象的時候,變量 i 是循環(huán)計數(shù)器 為 對象的屬性名, 以任意順序遍歷一個對象的可枚舉屬性。對于每個不同的屬性,語句都會被執(zhí)行。
當遍歷一個數(shù)組的時候,變量 i 是循環(huán)計數(shù)器 為 當前數(shù)組元素的索引不足:
for..in循環(huán)會把某個類型的原型(prototype)中方法與屬性給遍歷出來.
const array = ["admin","manager","db"];array.color = 'red';array.prototype.name= "zhangshan";for(var i in array){if(array.hasOwnProperty(i)){console.log(array[i]); // admin,manager,db,color}}// hasOwnProperty(): 對象的屬性或方法是非繼承的,返回truefor … of
迭代循環(huán)可迭代對象(包括Array,Map,Set,String,TypedArray,arguments 對象)等等。不能遍歷對象。只循環(huán)集合本身的元素
var a = ['A', 'B', 'C'];var s = new Set(['A', 'B', 'C']);var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);a.name = 'array';for (var x of a) {console.log(x); //'A', 'B', 'C'}for (var x of s) {console.log(x);//'A', 'B', 'C'}for (var x of m) {console.log(x[0] + '=' + x[1]);//1='x',2='y',3='z'}繼承 // 定義一個動物類function Animal(name) {// 屬性this.name = name || 'Animal';// 實例方法this.sleep = function(){console.log(this.name + '正在睡覺!');}}// 原型方法Animal.prototype.eat = function(food) {console.log(this.name + '正在吃:' + food);};原型鏈繼承
核心:?將父類的實例作為子類的原型。
function Dog(age) {this.age = age;}Dog.protoType = New Animal();Dog.prototype.name = 'dog';const dog = new Dog(12);console.log(dog.name);console.log(dog.eat('age'));console.log(dog instanceof Animal); //trueconsole.log(dog instanceof Dog); //truenew 創(chuàng)建新實例對象經(jīng)過了以下幾步:
1.創(chuàng)建一個新對象
2.將新對象的_proto_指向構造函數(shù)的prototype對象
3.將構造函數(shù)的作用域賦值給新對象 (也就是this指向新對象)
4.執(zhí)行構造函數(shù)中的代碼(為這個新對象添加屬性)
5.返回新的對象// 1. 創(chuàng)建一個新對象var Obj = {};// 2. 將新對象的_proto_指向構造函數(shù)的prototype對象Obj._proto_ = Animal.prototype();// 3. 執(zhí)行構造函數(shù)中的代碼(為這個新對象添加屬性)Animal.call(Obj);// 4. 返回新的對象return Obj;特點:
1.實例可繼承的屬性有:實例的構造函數(shù)的屬性,父類構造函數(shù)屬性,父類原型的屬性
2.非常純粹的繼承關系,實例是子類的實例,也是父類的實例
3.父類新增原型方法/原型屬性,子類都能訪問到缺點:
1.新實例無法向父類構造函數(shù)傳參。
2.繼承單一。
3.所有新實例都會共享父類實例的屬性。(原型上的屬性是共享的,一個實例修改了原型屬性,另一個實例的原型屬性也會被修改?。?br>4.要想為子類新增原型上的屬性和方法,必須要在new Animal()這樣的語句之后執(zhí)行,不能放到構造器中構造函數(shù)繼承
核心:使用父類的構造函數(shù)來增強子類實例,等于是復制父類的實例屬性給子類(沒用到原型)
function Dog(name) {Animal.apply(this, 'dog');this.name = name;}const dog = new Dog();console.log(dog.name);console.log(dog.eat('age'));console.log(dog instanceof Animal); //falseconsole.log(dog instanceof Dog); //true重點:用.call()和.apply()將父類構造函數(shù)引入子類函數(shù)(在子類函數(shù)中做了父類函數(shù)的自執(zhí)行(復制))
特點:
1.只繼承了父類構造函數(shù)的屬性,沒有繼承父類原型的屬性。
2.解決了原型鏈繼承缺點1、2、3。
3.可以實現(xiàn)多繼承,繼承多個構造函數(shù)屬性(call多個)。
4.在子實例中可向父實例傳參。缺點:
1.能繼承父類構造函數(shù)的屬性。
2.無法實現(xiàn)構造函數(shù)的復用。(每次用每次都要重新調(diào)用)
3.每個新實例都有父類構造函數(shù)的副本,臃腫。
4.實例并不是父類的實例,只是子類的實例組合繼承(原型鏈繼承和構造函數(shù)繼承)(常用)
核心:通過調(diào)用父類構造,繼承父類的屬性并保留傳參的優(yōu)點,然后通過將父類實例作為子類原型,實現(xiàn)函數(shù)復用
function Cat(name){Animal.call(this, name);this.name = name;}Cat.prototype = new Animal();Cat.prototype.constructor = Cat;var cat = new Cat();console.log(cat.name);console.log(cat.sleep());console.log(cat instanceof Animal); // trueconsole.log(cat instanceof Cat); // true重點:結合了兩種模式的優(yōu)點,傳參和復用
特點:
1.可以繼承父類原型上的屬性,可以傳參,可復用。
2.每個新實例引入的構造函數(shù)屬性是私有的。
3.既是子類的實例,也是父類的實例缺點:
調(diào)用了兩次父類構造函數(shù)(耗內(nèi)存),子類的構造函數(shù)會代替原型上的那個父類構造函數(shù)。原型式繼承

重點:用一個函數(shù)包裝一個對象,然后返回這個函數(shù)的調(diào)用,這個函數(shù)就變成了個可以隨意增添屬性的實例或?qū)ο?。object.create()就是這個原理。
特點:
類似于復制一個對象,用函數(shù)來包裝。缺點:
1.所有實例都會繼承原型上的屬性。
2.無法實現(xiàn)復用。(新實例屬性都是后面添加的)寄生式繼承

重點:就是給原型式繼承外面套了個殼子。
優(yōu)點:沒有創(chuàng)建自定義類型,因為只是套了個殼子返回對象(這個),這個函數(shù)順理成章就成了創(chuàng)建的新對象。
缺點:沒用到原型,無法復用。寄生組合式繼承(常用)
寄生:在函數(shù)內(nèi)返回對象然后調(diào)用
組合:
1、函數(shù)的原型等于另一個實例。
2、在函數(shù)中用apply或者call引入另一個構造函數(shù),可傳參。function Cat(name){Animal.call(this);this.name = name || 'Tom';}(function(){// 創(chuàng)建一個沒有實例方法的類var Super = function(){};Super.prototype = Animal.prototype;//將實例作為子類的原型Cat.prototype = new Super();})();var cat = new Cat();Cat.prototype.constructor = Cat; // 需要修復下構造函數(shù)console.log(cat.name);console.log(cat.sleep());console.log(cat instanceof Animal); // trueconsole.log(cat instanceof Cat); //true重點:修復了組合繼承的問題。
本文完? 
