Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 www.久久视频,91精品视频在线看,中文字幕视频网站

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          學(xué)習(xí)HTML的7個秘訣,網(wǎng)友:原來是這樣...

          學(xué)習(xí)HTML的7個秘訣,網(wǎng)友:原來是這樣...

          何學(xué)習(xí)HTML語言。

          初學(xué)者有一個常見的錯誤就是猶豫于判斷哪種編程語言是做好的、最該先學(xué)的。 我們有很多的選擇,但你不能說那種語言“最好”。

          我們應(yīng)該理解:說到底,什么語言并不重要。 重要的是理解數(shù)據(jù)結(jié)構(gòu)、控制邏輯和設(shè)計(jì)模式。 任何一種語言—甚至一種簡單的腳本語言—都會具有所有編程語言都共有的各種特征,也就是說各種語言是貫通的。

          我編程使用Pascal,匯編,和C語言,事實(shí)上我從來沒有把它當(dāng)成職業(yè)以求獲得回報。 我一直在自學(xué)編程,工作上用不到它,我使用現(xiàn)有的知識,參考各種文檔和書本,學(xué)習(xí)它們的用法。

          因此,不要急于選擇何種編程語言。 找出你想要開發(fā)的東西,使用一種能夠完成這項(xiàng)任務(wù)的語言,這就可以了。

          這里以html為例說一下:

          html的意義就在于放置內(nèi)容,無論是文本還是圖片,它們都屬于內(nèi)容。
          按照規(guī)定,所有的內(nèi)容都必須放在容器里,否則無法享受五折優(yōu)惠…對不起走錯片場了。而容器就是html的標(biāo)簽,語法結(jié)構(gòu)是:
          <容器名>各種文本內(nèi)容</容器名>

          容器名當(dāng)然是英文字母的,并且有許多種。它們的規(guī)則很簡單:
          1.一般成對出現(xiàn),一個表開始一個表結(jié)束
          2.結(jié)束的那個前面需要加一條斜線
          3.容器名放在尖角符號內(nèi)
          另外,容器之間可以嵌套,也就是說容器里面可以再放容器。

          到這里為止,你已經(jīng)可以開始寫html了,隨便百度一下html有哪些標(biāo)簽,然后跟著套用即可。

          但如果還是有點(diǎn)懵,可以按如下順序做一遍:
          1.找一個盡可能簡單的html源碼頁面看一遍,把每個標(biāo)簽干嘛的鬧清楚
          2.假裝有一篇word文檔要轉(zhuǎn)成html,親手寫一遍寫一個html頁面
          3.沒了,html入門真的就那么簡單……

          桌面腳本

          想要動手在Windows里或蘋果系統(tǒng)里編程,最簡單的方法是從一種腳本語言或宏語言開始,例如AutoHotkey(Windows) 或Automator(蘋果系統(tǒng))。 如今一些硬件程序員沖著他們的屏幕大喊大叫,說AHK和AppleScript并不是“真正”的編程語言。 也許他們說的是對的—技術(shù)上,這些種類的語言只能做一些上層的編程。 但是對于那些只是想來脫盲、想在他們的電腦里實(shí)現(xiàn)一些能自動運(yùn)行的程序的新手來說,這些語言會是一個絕妙的入門入口—而且你會吃驚于它們豐富的功能。

          Web開發(fā)

          HTML 和 CSS:開發(fā)網(wǎng)站,你第一件要知道的事情就是HTML(網(wǎng)頁就是由它組成的)和CSS(一種讓外觀更好看的樣式標(biāo)記)。 HTML 和 CSS 并不是編程語言—它們只是頁面的結(jié)構(gòu)和樣式信息。 然而,在開始開發(fā)web應(yīng)用程序之前你必須要學(xué)會如何手工的編寫簡單的HTML和CSS,web頁面是任何webapp的前端顯示部分。 這個HTML 指導(dǎo)是你入手的好地方。

          JavaScript:當(dāng)你可以通過HTML和CSS構(gòu)建一個靜態(tài)頁面后,事情就開始變得有趣了—因?yàn)榈搅嗽搶W(xué)JavaScript的時候了。 JavaScript是一種web瀏覽器上的編程語言,它的魔力就是能在頁面里制造一些動態(tài)效果。

          JavaScript可以做bookmarklets,Greasemonkey腳本, 和Ajax, 所以它是web上各種好東西的關(guān)于因素。學(xué)習(xí)JavaScript從這里開。

          服務(wù)器端腳本:

          一旦你學(xué)會了網(wǎng)頁里的知識,你就要開始對它添加一些動態(tài)服務(wù)器操作—為了實(shí)現(xiàn)這些,你需要把目光轉(zhuǎn)移到服務(wù)器端腳本語言,例如PHP, Python, Perl, 或 Ruby。

          舉個例子,如果想要制作一個網(wǎng)頁形式的聯(lián)系方式表單,根據(jù)用戶的輸入發(fā)送郵件,你就需要使用服務(wù)器端腳本來實(shí)現(xiàn)。 像PHP這樣的腳本語言可以讓你跟web服務(wù)器上的數(shù)據(jù)庫進(jìn)行溝通,所以如果你想搭建一個用戶可以登錄注冊的網(wǎng)站,這樣的語言正是你需要的。

          Web框架:

          過去數(shù)年里,web開發(fā)人員在開發(fā)動態(tài)網(wǎng)站的過程中不得不一遍又一遍的針對重復(fù)遇到的問題寫出重復(fù)的代碼。 為了避免這種每次開發(fā)一些新網(wǎng)站都會重復(fù)勞動一次的問題,一些程序員動手搭建了一些框架,讓框架替我們完成重復(fù)性的工作。

          非常流行的Ruby on Rails框架,作為一個例子,它利用Ruby編程語言,為我們提供了一個專門面向web的架構(gòu),普通的web應(yīng)用程序都能使用它來完成。 事實(shí)上,Adam使用Rails開發(fā)了他的第一個正式的(而且是嘆為觀止的!)web應(yīng)用程序,MixTape.me。這就是他的如何在沒有任何經(jīng)驗(yàn)的情況下搭建一個網(wǎng)站。還有一些其他的web開發(fā)框架包括CakePHP(針對 PHP 編程者),Django(針對 Python 編程中), 以及jQuery(針對 JavaScript).

          Web APIs:

          API (應(yīng)用層序編程接口)是指不同的軟件之間相互交換的程序途徑。 例如,如果你想在你的網(wǎng)站上放一個動態(tài)的地圖,你可以使用Google Map,而不需要開發(fā)自己的地圖。The Google Maps API可以輕松的讓你通過JavaScript在程序中引入一個地圖到你的頁面上。 幾乎所有的現(xiàn)代的你所知道的和喜愛的web服務(wù)都提供了API,通過這些API你可以獲取到他們的數(shù)據(jù)和小工具,在你的應(yīng)用程序里就可以使用這些交互過來的東西了,例如Twitter, Facebook, Google Docs, Google Maps, 這個列表遠(yuǎn)不止這些。

          通過API把其他web應(yīng)用集成到你的web應(yīng)用里是現(xiàn)在富web開發(fā)的前沿地帶。 每個優(yōu)秀的主流的web服務(wù)API都附帶有完整的文檔和一些快速入手的指導(dǎo)(例如,這個就是Twitter的)。 瘋狂吧。

          命令行腳本

          如果你想開發(fā)一個程序,讓它讀取文字或文件、輸入輸出一些有用的東西,那么,命令行腳本語言將是個不錯的選擇。 然而它并不像web應(yīng)用程序和桌面應(yīng)用程序那樣有吸引力和好看的外觀,但是作為快速開發(fā)的腳本語言,你卻不能忽視它們。

          很多的在linux平臺上運(yùn)行的web腳本同樣能以命令行模式運(yùn)行,例如Perl,Python和PHP,所以如果你學(xué)會了使用它們,你將能在兩種環(huán)境中使用它們。 我的學(xué)習(xí)道路一直沒離開Perl太遠(yuǎn),我自學(xué)Python使用的是這本優(yōu)秀的在線免費(fèi)書Dive into Python。

          如果成為一個Unix高手也是你學(xué)習(xí)的目標(biāo),那么你絕對要精通bash這個腳本語言。 Bash是Unix和Linux環(huán)境下的一種命令行腳本語言,它能夠?yàn)槟阕鏊缘氖虑椋簭淖詣觽浞輸?shù)據(jù)庫腳本到功能齊全的用戶交互程序。 起初我沒有任何使用bash腳本的經(jīng)驗(yàn),但最終我用bash開發(fā)了一個全功能的個人代辦任務(wù)管理器:Todo.txt CLI。

          插件(Add-ons)

          如今的web應(yīng)用程序和瀏覽器都可以通過一些擴(kuò)展軟件來豐富自己的功能。 由于一些現(xiàn)有的軟件,例如Firefox、WordPress越來越受到開發(fā)人員的關(guān)注,插件的開發(fā)也日益流行,人們都在說“But if only it could do THIS…”

          只要你掌握了HTML,JavaScript和CSS,你就可以在任何的瀏覽器里開發(fā)你想要的很多東西。 Bookmarklets,Greasemonkeyuser scripts, 和Stylishuser styles這些軟件都是用的更普通頁面一樣的語言寫成的, 這幾個東西都值得你去研究一些。

          更高級的瀏覽器擴(kuò)展程序,例如Firefox的擴(kuò)展,它們可以幫助你很多。 開發(fā)Firefox的擴(kuò)展.

          舉個例子,需要你精通JavaScript和XML(一種標(biāo)記語言,類似HTML,但具有更嚴(yán)格的格式)。 早在2007年我就寫下來how to build a Firefox extension, 這是我在笨手笨腳的研究網(wǎng)上的一些學(xué)習(xí)資料后獲得的成果。

          很多免費(fèi)的、受歡迎的web應(yīng)用程序都提供了擴(kuò)展框架,例如WordPress 和 MediaWiki。 這些應(yīng)用程序都是用PHP寫成的,所以只有對PHP熟悉你才能做這些事情。 這個就是如何編寫WordPress插件。 而想駕馭Google Wave前沿技術(shù)的開發(fā)人員可以從使用HTML, JavaScript, Java, 和 Python 寫小組件和小工具開始。 我寫的第一個Wave bot是跟著這個一個下午時間的快速入門指導(dǎo)開始的。

          開發(fā)桌面上的Web應(yīng)用程序

          學(xué)習(xí)編程最好的結(jié)果是你在一個環(huán)境下學(xué)的東西可以應(yīng)用到另外的環(huán)境中。 先學(xué)習(xí)開發(fā)web應(yīng)用程序的好處就是我們有一些方法可以讓web應(yīng)用程序直接在桌面上運(yùn)行。 例如,Adobe AIR是一個跨平臺的即時運(yùn)行平臺,它能讓你編寫的程序運(yùn)行在任何裝有AIR的操作系統(tǒng)的桌面上。

          AIR應(yīng)用程序都是由HTML, Flash, 或 Flex 寫成的,所以它能讓你的web程序在桌面環(huán)境中運(yùn)行。 AIR是開發(fā)部署桌面應(yīng)用程序的一個優(yōu)秀的選擇,就像我們提到過的10個讓你值得去安裝AIR的應(yīng)用程序。

          - 移動應(yīng)用開發(fā)

          能在iPhone或者Android智能手機(jī)上運(yùn)行的手機(jī)應(yīng)用程序的開發(fā)如今正呈現(xiàn)井噴之勢,所以你也可以夢想一下如何在iTunes應(yīng)用商店里通過你的天才程序大賺一筆。 但是,作為一個編碼新手,直接奔向移動開發(fā)所經(jīng)歷的學(xué)習(xí)曲線可能會很陡,因?yàn)樗枰闶煜じ呒壍木幊陶Z言,例如Java和Objective C。

          然而,你當(dāng)然應(yīng)該看看iPhone 和 Android 編程究竟是什么樣子的。 閱讀這個簡單的iPhone應(yīng)用開發(fā)例子可以初步認(rèn)識一下iPhone程序的開發(fā)過程。 Android 程序都是由Java寫成的,這有一個簡單的視頻教程教你如何開發(fā)第一個”Hello Android“程序

          耐心,刻苦,嘗試,失敗

          作為一個好的程序員,都有一個不達(dá)目的誓不罷休的品質(zhì),他們會驚喜于通過長期推敲和失敗換來的一點(diǎn)成績。 學(xué)會編程會有很好的回報的,但是學(xué)習(xí)的過程可能會是飽受挫折和孤獨(dú)的。 如果有可能,最好找個伴一起陪你做這件事。 想精通編程,這和其他事情一樣,需要堅(jiān)持,反復(fù)嘗試,獲得更多的經(jīng)驗(yàn)。


          his是JavaScript中的一個關(guān)鍵字,但是又一個相對比較特別的關(guān)鍵字,不像function、var、for、if這些關(guān)鍵字一樣,可以很清楚的搞清楚它到底是如何使用的。

          this會在執(zhí)行上下文中綁定一個對象,但是是根據(jù)什么條件綁定的呢?在不同的執(zhí)行條件下會綁定不同的對象,這也是讓人捉摸不定的地方。

          這一次,我們一起來徹底搞定this到底是如何綁定的吧!

          一. 理解this

          1.1. 為什么使用this

          在常見的編程語言中,幾乎都有this這個關(guān)鍵字(Objective-C中使用的是self),但是JavaScript中的this和常見的面向?qū)ο笳Z言中的this不太一樣:

          • 常見面向?qū)ο蟮木幊陶Z言中,比如Java、C++、Swift、Dart等等一系列語言中,this通常只會出現(xiàn)在類的方法中。
          • 也就是你需要有一個類,類中的方法(特別是實(shí)例方法)中,this代表的是當(dāng)前調(diào)用對象。
          • 但是JavaScript中的this更加靈活,無論是它出現(xiàn)的位置還是它代表的含義。

          使用this有什么意義呢?下面的代碼中,我們通過對象字面量創(chuàng)建出來一個對象,當(dāng)我們調(diào)用對象的方法時,希望將對象的名稱一起進(jìn)行打印。

          如果沒有this,那么我們的代碼會是下面的寫法:

          • 在方法中,為了能夠獲取到name名稱,必須通過obj的引用(變量名稱)來獲取。
          • 但是這樣做有一個很大的弊端:如果我將obj的名稱換成了info,那么所有的方法中的obj都需要換成info。
          var obj={
            name: "why",
            running: function() {
              console.log(obj.name + " running");
            },
            eating: function() {
              console.log(obj.name + " eating");
            },
            studying: function() {
              console.log(obj.name + " studying");
            }
          }
          

          事實(shí)上,上面的代碼,在實(shí)際開發(fā)中,我們都會使用this來進(jìn)行優(yōu)化:

          • 當(dāng)我們通過obj去調(diào)用running、eating、studying這些方法時,this就是指向的obj對象
          var obj={
            name: "why",
            running: function() {
              console.log(this.name + " running");
            },
            eating: function() {
              console.log(this.name + " eating");
            },
            studying: function() {
              console.log(this.name + " studying");
            }
          }
          

          所以我們會發(fā)現(xiàn),在某些函數(shù)或者方法的編寫中,this可以讓我們更加便捷的方式來引用對象,在進(jìn)行一些API設(shè)計(jì)時,代碼更加的簡潔和易于復(fù)用。

          當(dāng)然,上面只是應(yīng)用this的一個場景而已,開發(fā)中使用到this的場景到處都是,這也是為什么它不容易理解的原因。

          1.2. this指向什么

          我們先說一個最簡單的,this在全局作用域下指向什么?

          • 這個問題非常容易回答,在瀏覽器中測試就是指向window
          • 所以,在全局作用域下,我們可以認(rèn)為this就是指向的window
          console.log(this); // window
          
          var name="why";
          console.log(this.name); // why
          console.log(window.name); // why
          

          但是,開發(fā)中很少直接在全局作用域下去使用this,通常都是在函數(shù)中使用

          所有的函數(shù)在被調(diào)用時,都會創(chuàng)建一個執(zhí)行上下文:

          • 這個上下文中記錄著函數(shù)的調(diào)用棧、函數(shù)的調(diào)用方式、傳入的參數(shù)信息等;
          • this也是其中的一個屬性;

          我們先來看一個讓人困惑的問題:

          • 定義一個函數(shù),我們采用三種不同的方式對它進(jìn)行調(diào)用,它產(chǎn)生了三種不同的結(jié)果
          // 定義一個函數(shù)
          function foo() {
            console.log(this);
          }
          
          // 1.調(diào)用方式一: 直接調(diào)用
          foo(); // window
          
          // 2.調(diào)用方式二: 將foo放到一個對象中,再調(diào)用
          var obj={
            name: "why",
            foo: foo
          }
          obj.foo() // obj對象
          
          // 3.調(diào)用方式三: 通過call/apply調(diào)用
          foo.call("abc"); // String {"abc"}對象
          

          上面的案例可以給我們什么樣的啟示呢?

          • 1.函數(shù)在調(diào)用時,JavaScript會默認(rèn)給this綁定一個值;
          • 2.this的綁定和定義的位置(編寫的位置)沒有關(guān)系;
          • 3.this的綁定和調(diào)用方式以及調(diào)用的位置有關(guān)系;
          • 4.this是在運(yùn)行時被綁定的;

          那么this到底是怎么樣的綁定規(guī)則呢?一起來學(xué)習(xí)一下吧

          二. this綁定規(guī)則

          我們現(xiàn)在已經(jīng)知道this無非就是在函數(shù)調(diào)用時被綁定的一個對象,我們就需要知道它在不同的場景下的綁定規(guī)則即可。

          2.1. 默認(rèn)綁定

          什么情況下使用默認(rèn)綁定呢?獨(dú)立函數(shù)調(diào)用。

          • 獨(dú)立的函數(shù)調(diào)用我們可以理解成函數(shù)沒有被綁定到某個對象上進(jìn)行調(diào)用;

          案例一:普通函數(shù)調(diào)用

          • 該函數(shù)直接被調(diào)用,并沒有進(jìn)行任何的對象關(guān)聯(lián);
          • 這種獨(dú)立的函數(shù)調(diào)用會使用默認(rèn)綁定,通常默認(rèn)綁定時,函數(shù)中的this指向全局對象(window);
          function foo() {
            console.log(this); // window
          }
          
          foo();
          

          案例二:函數(shù)調(diào)用鏈(一個函數(shù)調(diào)用另外一個函數(shù))

          • 所有的函數(shù)調(diào)用都沒有被綁定到某個對象上;
          // 2.案例二:
          function test1() {
            console.log(this); // window
            test2();
          }
          
          function test2() {
            console.log(this); // window
            test3()
          }
          
          function test3() {
            console.log(this); // window
          }
          test1();
          

          案例三:將函數(shù)作為參數(shù),傳入到另一個函數(shù)中

          function foo(func) {
            func()
          }
          
          function bar() {
            console.log(this); // window
          }
          
          foo(bar);
          

          我們對案例進(jìn)行一些修改,考慮一下打印結(jié)果是否會發(fā)生變化:

          • 這里的結(jié)果依然是window,為什么呢?
          • 原因非常簡單,在真正函數(shù)調(diào)用的位置,并沒有進(jìn)行任何的對象綁定,只是一個獨(dú)立函數(shù)的調(diào)用;
          function foo(func) {
            func()
          }
          
          var obj={
            name: "why",
            bar: function() {
              console.log(this); // window
            }
          }
          
          foo(obj.bar);
          

          2.2. 隱式綁定

          另外一種比較常見的調(diào)用方式是通過某個對象進(jìn)行調(diào)用的:

          • 也就是說它的調(diào)用位置中,是通過某個對象發(fā)起的函數(shù)調(diào)用。

          案例一:通過對象調(diào)用函數(shù)

          • foo的調(diào)用位置是obj.foo()方式進(jìn)行調(diào)用的
          • 那么foo調(diào)用時this會隱式地被綁定到obj對象上
          function foo() {
            console.log(this); // obj對象
          }
          
          var obj={
            name: "why",
            foo: foo
          }
          
          obj.foo();
          

          案例二:案例一的變化

          • 我們通過obj2又引用了obj1對象,再通過obj1對象調(diào)用foo函數(shù);
          • 那么foo調(diào)用的位置上其實(shí)還是obj1被綁定了this;
          function foo() {
            console.log(this); // obj對象
          }
          
          var obj1={
            name: "obj1",
            foo: foo
          }
          
          var obj2={
            name: "obj2",
            obj1: obj1
          }
          
          obj2.obj1.foo();
          

          案例三:隱式丟失

          • 結(jié)果最終是window,為什么是window呢?
          • 因?yàn)閒oo最終被調(diào)用的位置是bar,而bar在進(jìn)行調(diào)用時沒有綁定任何的對象,也就沒有形成隱式綁定;
          • 相當(dāng)于是一種默認(rèn)綁定;
          function foo() {
            console.log(this);
          }
          
          var obj1={
            name: "obj1",
            foo: foo
          }
          
          // 講obj1的foo賦值給bar
          var bar=obj1.foo;
          bar();
          

          2.3. 顯示綁定

          隱式綁定有一個前提條件:

          • 必須在調(diào)用的對象內(nèi)部有一個對函數(shù)的引用(比如一個屬性);
          • 如果沒有這樣的引用,在進(jìn)行調(diào)用時,會報找不到該函數(shù)的錯誤;
          • 正是通過這個引用,間接地將this綁定到了這個對象上;

          如果我們不希望在 對象內(nèi)部 包含這個函數(shù)的引用,同時又希望在這個對象上進(jìn)行強(qiáng)制調(diào)用,該怎么做呢?

          • JavaScript所有的函數(shù)都可以使用call和apply方法(這個和Prototype有關(guān))。
            • 它們兩個的區(qū)別這里不再展開;
            • 其實(shí)非常簡單,第一個參數(shù)是相同的,后面的參數(shù),apply為數(shù)組,call為參數(shù)列表;
          • 這兩個函數(shù)的第一個參數(shù)都要求是一個對象,這個對象的作用是什么呢?就是給this準(zhǔn)備的。
          • 在調(diào)用這個函數(shù)時,會將this綁定到這個傳入的對象上。

          因?yàn)樯厦娴倪^程,我們明確地綁定了this指向的對象,所以稱之為 顯示綁定

          2.3.1. call、apply

          通過call或者apply綁定this對象

          • 顯示綁定后,this就會明確地指向綁定的對象
          function foo() {
            console.log(this);
          }
          
          foo.call(window); // window
          foo.call({name: "why"}); // {name: "why"}
          foo.call(123); // Number對象,存放時123
          

          2.3.2. bind函數(shù)

          如果我們希望一個函數(shù)總是顯示的綁定到一個對象上,可以怎么做呢?

          方案一:自己手寫一個輔助函數(shù)(了解)

          • 我們手動寫了一個bind的輔助函數(shù)
          • 這個輔助函數(shù)的目的是在執(zhí)行foo時,總是讓它的this綁定到obj對象上
          function foo() {
            console.log(this);
          }
          
          var obj={
            name: "why"
          }
          
          function bind(func, obj) {
            return function() {
              return func.apply(obj, arguments);
            }
          }
          
          var bar=bind(foo, obj);
          
          bar(); // obj對象
          bar(); // obj對象
          bar(); // obj對象
          

          方案二:使用Function.prototype.bind

          function foo() {
            console.log(this);
          }
          
          var obj={
            name: "why"
          }
          
          var bar=foo.bind(obj);
          
          bar(); // obj對象
          bar(); // obj對象
          bar(); // obj對象
          

          2.3.3. 內(nèi)置函數(shù)

          有些時候,我們會調(diào)用一些JavaScript的內(nèi)置函數(shù),或者一些第三方庫中的內(nèi)置函數(shù)。

          • 這些內(nèi)置函數(shù)會要求我們傳入另外一個函數(shù);
          • 我們自己并不會顯示的調(diào)用這些函數(shù),而且JavaScript內(nèi)部或者第三方庫內(nèi)部會幫助我們執(zhí)行;
          • 這些函數(shù)中的this又是如何綁定的呢?

          案例一:setTimeout

          • setTimeout中會傳入一個函數(shù),這個函數(shù)中的this通常是window
          setTimeout(function() {
            console.log(this); // window
          }, 1000);
          

          為什么這里是window呢?

          • 這個和setTimeout源碼的內(nèi)部調(diào)用有關(guān);
          • setTimeout內(nèi)部是通過apply進(jìn)行綁定的this對象,并且綁定的是全局對象;

          案例二:數(shù)組的forEach

          數(shù)組有一個高階函數(shù)forEach,用于函數(shù)的遍歷:

          • 在forEach中傳入的函數(shù)打印的也是Window對象;
          • 這是因?yàn)槟J(rèn)情況下傳入的函數(shù)是自動調(diào)用函數(shù)(默認(rèn)綁定);
          var names=["abc", "cba", "nba"];
          names.forEach(function(item) {
            console.log(this); // 三次window
          });
          

          我們是否可以改變該函數(shù)的this指向呢?

          forEach參數(shù)

          var names=["abc", "cba", "nba"];
          var obj={name: "why"};
          names.forEach(function(item) {
            console.log(this); // 三次obj對象
          }, obj);
          

          案例三:div的點(diǎn)擊

          如果我們有一個div元素:

          • 注意:省略了部分代碼
            <style>
              .box {
                width: 200px;
                height: 200px;
                background-color: red;
              }
            </style>
          
            <div class="box"></div>
          

          獲取元素節(jié)點(diǎn),并且監(jiān)聽點(diǎn)擊:

          • 在點(diǎn)擊事件的回調(diào)中,this指向誰呢?box對象;
          • 這是因?yàn)樵诎l(fā)生點(diǎn)擊時,執(zhí)行傳入的回調(diào)函數(shù)被調(diào)用時,會將box對象綁定到該函數(shù)中;
          var box=document.querySelector(".box");
          box.onclick=function() {
            console.log(this); // box對象
          }
          

          所以傳入到內(nèi)置函數(shù)的回調(diào)函數(shù)this如何確定呢?

          • 某些內(nèi)置的函數(shù),我們很難確定它內(nèi)部是如何調(diào)用傳入的回調(diào)函數(shù);
          • 一方面可以通過分析源碼來確定,另一方面我們可以通過經(jīng)驗(yàn)(見多識廣)來確定;
          • 但是無論如何,通常都是我們之前講過的規(guī)則來確定的;

          2.4. new綁定

          JavaScript中的函數(shù)可以當(dāng)做一個類的構(gòu)造函數(shù)來使用,也就是使用new關(guān)鍵字。

          使用new關(guān)鍵字來調(diào)用函數(shù)時,會執(zhí)行如下的操作:

          • 1.創(chuàng)建一個全新的對象;
          • 2.這個新對象會被執(zhí)行Prototype連接;
          • 3.這個新對象會綁定到函數(shù)調(diào)用的this上(this的綁定在這個步驟完成);
          • 4.如果函數(shù)沒有返回其他對象,表達(dá)式會返回這個新對象;
          // 創(chuàng)建Person
          function Person(name) {
            console.log(this); // Person {}
            this.name=name; // Person {name: "why"}
          }
          
          var p=new Person("why");
          console.log(p);
          

          2.5. 規(guī)則優(yōu)先級

          學(xué)習(xí)了四條規(guī)則,接下來開發(fā)中我們只需要去查找函數(shù)的調(diào)用應(yīng)用了哪條規(guī)則即可,但是如果一個函數(shù)調(diào)用位置應(yīng)用了多條規(guī)則,優(yōu)先級誰更高呢?

          1.默認(rèn)規(guī)則的優(yōu)先級最低

          毫無疑問,默認(rèn)規(guī)則的優(yōu)先級是最低的,因?yàn)榇嬖谄渌?guī)則時,就會通過其他規(guī)則的方式來綁定this

          2.顯示綁定優(yōu)先級高于隱式綁定

          顯示綁定和隱式綁定哪一個優(yōu)先級更高呢?這個我們可以測試一下:

          • 結(jié)果是obj2,說明是顯示綁定生效了
          function foo() {
            console.log(this);
          }
          
          var obj1={
            name: "obj1",
            foo: foo
          }
          
          var obj2={
            name: "obj2",
            foo: foo
          }
          
          // 隱式綁定
          obj1.foo(); // obj1
          obj2.foo(); // obj2
          
          // 隱式綁定和顯示綁定同時存在
          obj1.foo.call(obj2); // obj2, 說明顯式綁定優(yōu)先級更高
          

          3.new綁定優(yōu)先級高于隱式綁定

          • 結(jié)果是foo,說明是new綁定生效了
          function foo() {
            console.log(this);
          }
          
          var obj={
            name: "why",
            foo: foo
          }
          
          new obj.foo(); // foo對象, 說明new綁定優(yōu)先級更高
          

          4.new綁定優(yōu)先級高于bind

          new綁定和call、apply是不允許同時使用的,所以不存在誰的優(yōu)先級更高

          function foo() {
            console.log(this);
          }
          
          var obj={
            name: "obj"
          }
          
          var foo=new foo.call(obj);
          

          new和call同時使用

          但是new綁定是否可以和bind后的函數(shù)同時使用呢?可以

          • 結(jié)果顯示為foo,那么說明是new綁定生效了
          function foo() {
            console.log(this);
          }
          
          var obj={
            name: "obj"
          }
          
          // var foo=new foo.call(obj);
          var bar=foo.bind(obj);
          var foo=new bar(); // 打印foo, 說明使用的是new綁定
          

          優(yōu)先級總結(jié):

          • new綁定 > 顯示綁定(bind)> 隱式綁定 > 默認(rèn)綁定

          三. this規(guī)則之外

          我們講到的規(guī)則已經(jīng)足以應(yīng)付平時的開發(fā),但是總有一些語法,超出了我們的規(guī)則之外。(神話故事和動漫中總是有類似這樣的人物)

          3.1. 忽略顯示綁定

          如果在顯示綁定中,我們傳入一個null或者undefined,那么這個顯示綁定會被忽略,使用默認(rèn)規(guī)則:

          function foo() {
            console.log(this);
          }
          
          var obj={
            name: "why"
          }
          
          foo.call(obj); // obj對象
          foo.call(null); // window
          foo.call(undefined); // window
          
          var bar=foo.bind(null);
          bar(); // window
          

          3.2. 間接函數(shù)引用

          另外一種情況,創(chuàng)建一個函數(shù)的 間接引用,這種情況使用默認(rèn)綁定規(guī)則。

          我們先來看下面的案例結(jié)果是什么?

          • (num2=num1)的結(jié)果是num1的值;
          var num1=100;
          var num2=0;
          var result=(num2=num1);
          console.log(result); // 100
          

          我們來下面的函數(shù)賦值結(jié)果:

          • 賦值(obj2.foo=obj1.foo)的結(jié)果是foo函數(shù);
          • foo函數(shù)被直接調(diào)用,那么是默認(rèn)綁定;
          function foo() {
            console.log(this);
          }
          
          var obj1={
            name: "obj1",
            foo: foo
          }; 
          
          var obj2={
            name: "obj2"
          }
          
          obj1.foo(); // obj1對象
          (obj2.foo=obj1.foo)();  // window
          

          3.3. ES6箭頭函數(shù)

          在ES6中新增一個非常好用的函數(shù)類型:箭頭函數(shù)

          • 這里不再具體介紹箭頭函數(shù)的用法,可以自行學(xué)習(xí)。

          箭頭函數(shù)不使用this的四種標(biāo)準(zhǔn)規(guī)則(也就是不綁定this),而是根據(jù)外層作用域來決定this。

          我們來看一個模擬網(wǎng)絡(luò)請求的案例:

          • 這里我使用setTimeout來模擬網(wǎng)絡(luò)請求,請求到數(shù)據(jù)后如何可以存放到data中呢?
          • 我們需要拿到obj對象,設(shè)置data;
          • 但是直接拿到的this是window,我們需要在外層定義:var _this=this
          • 在setTimeout的回調(diào)函數(shù)中使用_this就代表了obj對象
          var obj={
            data: [],
            getData: function() {
              var _this=this;
              setTimeout(function() {
                // 模擬獲取到的數(shù)據(jù)
                var res=["abc", "cba", "nba"];
                _this.data.push(...res);
              }, 1000);
            }
          }
          
          obj.getData();
          

          上面的代碼在ES6之前是我們最常用的方式,從ES6開始,我們會使用箭頭函數(shù):

          • 為什么在setTimeout的回調(diào)函數(shù)中可以直接使用this呢?
          • 因?yàn)榧^函數(shù)并不綁定this對象,那么this引用就會從上層作用域中找到對應(yīng)的this
          var obj={
            data: [],
            getData: function() {
              setTimeout(()=> {
                // 模擬獲取到的數(shù)據(jù)
                var res=["abc", "cba", "nba"];
                this.data.push(...res);
              }, 1000);
            }
          }
          
          obj.getData();
          

          思考:如果getData也是一個箭頭函數(shù),那么setTimeout中的回調(diào)函數(shù)中的this指向誰呢?

          • 答案是window;
          • 依然是不斷的從上層作用域找,那么找到了全局作用域;
          • 在全局作用域內(nèi),this代表的就是window
          var obj={
            data: [],
            getData: ()=> {
              setTimeout(()=> {
                console.log(this); // window
              }, 1000);
            }
          }
          
          obj.getData();
          

          四. this面試題

          4.1. 面試題一:

          var name="window";
          var person={
            name: "person",
            sayName: function () {
              console.log(this.name);
            }
          };
          function sayName() {
            var sss=person.sayName;
            sss(); 
            person.sayName(); 
            (person.sayName)(); 
            (b=person.sayName)(); 
          }
          sayName();
          

          這道面試題非常簡單,無非就是繞一下,希望把面試者繞暈:

          function sayName() {
            var sss=person.sayName;
            // 獨(dú)立函數(shù)調(diào)用,沒有和任何對象關(guān)聯(lián)
            sss(); // window
            // 關(guān)聯(lián)
            person.sayName(); // person
            (person.sayName)(); // person
            (b=person.sayName)(); // window
          }
          

          4.2. 面試題二:

          var name='window'
          var person1={
            name: 'person1',
            foo1: function () {
              console.log(this.name)
            },
            foo2: ()=> console.log(this.name),
            foo3: function () {
              return function () {
                console.log(this.name)
              }
            },
            foo4: function () {
              return ()=> {
                console.log(this.name)
              }
            }
          }
          
          var person2={ name: 'person2' }
          
          person1.foo1(); 
          person1.foo1.call(person2); 
          
          person1.foo2();
          person1.foo2.call(person2);
          
          person1.foo3()();
          person1.foo3.call(person2)();
          person1.foo3().call(person2);
          
          person1.foo4()();
          person1.foo4.call(person2)();
          person1.foo4().call(person2);
          

          下面是代碼解析:

          、輸入地址

          當(dāng)我們開始在瀏覽器中輸入網(wǎng)址的時候,瀏覽器其實(shí)就已經(jīng)在智能的匹配可能得 url 了,他會從歷史記錄,書簽等地方,找到已經(jīng)輸入的字符串可能對應(yīng)的 url,然后給出智能提示,讓你可以補(bǔ)全url地址。對于 google的chrome 的瀏覽器,他甚至?xí)苯訌木彺嬷邪丫W(wǎng)頁展示出來,就是說,你還沒有按下 enter,頁面就出來了。


          2、瀏覽器查找域名的 IP 地址  

          1、請求一旦發(fā)起,瀏覽器首先要做的事情就是解析這個域名,一般來說,瀏覽器會首先查看本地硬盤的 hosts 文件,看看其中有沒有和這個域名對應(yīng)的規(guī)則,如果有的話就直接使用 hosts 文件里面的 ip 地址。

          2、如果在本地的 hosts 文件沒有能夠找到對應(yīng)的 ip 地址,瀏覽器會發(fā)出一個 DNS請求到本地DNS服務(wù)器 。本地DNS服務(wù)器一般都是你的網(wǎng)絡(luò)接入服務(wù)器商提供,比如中國電信,中國移動。

          3、查詢你輸入的網(wǎng)址的DNS請求到達(dá)本地DNS服務(wù)器之后,本地DNS服務(wù)器會首先查詢它的緩存記錄,如果緩存中有此條記錄,就可以直接返回結(jié)果,此過程是遞歸的方式進(jìn)行查詢。如果沒有,本地DNS服務(wù)器還要向DNS根服務(wù)器進(jìn)行查詢。

          4、根DNS服務(wù)器沒有記錄具體的域名和IP地址的對應(yīng)關(guān)系,而是告訴本地DNS服務(wù)器,你可以到域服務(wù)器上去繼續(xù)查詢,并給出域服務(wù)器的地址。這種過程是迭代的過程。

          5、本地DNS服務(wù)器繼續(xù)向域服務(wù)器發(fā)出請求,在這個例子中,請求的對象是.com域服務(wù)器。.com域服務(wù)器收到請求之后,也不會直接返回域名和IP地址的對應(yīng)關(guān)系,而是告訴本地DNS服務(wù)器,你的域名的解析服務(wù)器的地址。

          6、最后,本地DNS服務(wù)器向域名的解析服務(wù)器發(fā)出請求,這時就能收到一個域名和IP地址對應(yīng)關(guān)系,本地DNS服務(wù)器不僅要把IP地址返回給用戶電腦,還要把這個對應(yīng)關(guān)系保存在緩存中,以備下次別的用戶查詢時,可以直接返回結(jié)果,加快網(wǎng)絡(luò)訪問。

          下面這張圖很完美的解釋了這一過程:


          —知識擴(kuò)展—

          1.什么是DNS?

          DNS(Domain Name System,域名系統(tǒng)),因特網(wǎng)上作為域名和IP地址相互映射的一個分布式數(shù)據(jù)庫,能夠使用戶更方便的訪問互聯(lián)網(wǎng),而不用去記住能夠被機(jī)器直接讀取的IP數(shù)串。通過主機(jī)名,最終得到該主機(jī)名對應(yīng)的IP地址的過程叫做域名解析(或主機(jī)名解析)。
            
          通俗的講,我們更習(xí)慣于記住一個網(wǎng)站的名字,比如www.baidu.com,而不是記住它的ip地址,比如:167.23.10.2。而計(jì)算機(jī)更擅長記住網(wǎng)站的ip地址,而不是像www.baidu.com等鏈接。因?yàn)椋珼NS就相當(dāng)于一個電話本,比如你要找www.baidu.com這個域名,那我翻一翻我的電話本,我就知道,哦,它的電話(ip)是167.23.10.2。

          2.DNS查詢的兩種方式:遞歸查詢和迭代查詢

          1、遞歸解析

          當(dāng)局部DNS服務(wù)器自己不能回答客戶機(jī)的DNS查詢時,它就需要向其他DNS服務(wù)器進(jìn)行查詢。此時有兩種方式,如圖所示的是遞歸方式。局部DNS服務(wù)器自己負(fù)責(zé)向其他DNS服務(wù)器進(jìn)行查詢,一般是先向該域名的根域服務(wù)器查詢,再由根域名服務(wù)器一級級向下查詢。最后得到的查詢結(jié)果返回給局部DNS服務(wù)器,再由局部DNS服務(wù)器返回給客戶端。

          2、迭代解析

          當(dāng)局部DNS服務(wù)器自己不能回答客戶機(jī)的DNS查詢時,也可以通過迭代查詢的方式進(jìn)行解析,如圖所示。局部DNS服務(wù)器不是自己向其他DNS服務(wù)器進(jìn)行查詢,而是把能解析該域名的其他DNS服務(wù)器的IP地址返回給客戶端DNS程序,客戶端DNS程序再繼續(xù)向這些DNS服務(wù)器進(jìn)行查詢,直到得到查詢結(jié)果為止。也就是說,迭代解析只是幫你找到相關(guān)的服務(wù)器而已,而不會幫你去查。比如說:baidu.com的服務(wù)器ip地址在192.168.4.5這里,你自己去查吧,本人比較忙,只能幫你到這里了。

          3.DNS域名稱空間的組織方式

          我們在前面有說到根DNS服務(wù)器,域DNS服務(wù)器,這些都是DNS域名稱空間的組織方式。按其功能命名空間中用來描述 DNS 域名稱的五個類別的介紹詳見下表中,以及與每個名稱類型的示例

          4.DNS負(fù)載均衡

          當(dāng)一個網(wǎng)站有足夠多的用戶的時候,假如每次請求的資源都位于同一臺機(jī)器上面,那么這臺機(jī)器隨時可能會蹦掉。處理辦法就是用DNS負(fù)載均衡技術(shù),它的原理是在DNS服務(wù)器中為同一個主機(jī)名配置多個IP地址,在應(yīng)答DNS查詢時,DNS服務(wù)器對每個查詢將以DNS文件中主機(jī)記錄的IP地址按順序返回不同的解析結(jié)果,將客戶端的訪問引導(dǎo)到不同的機(jī)器上去,使得不同的客戶端訪問不同的服務(wù)器,從而達(dá)到負(fù)載均衡的目的?例如可以根據(jù)每臺機(jī)器的負(fù)載量,該機(jī)器離用戶地理位置的距離等等。


          3、瀏覽器向 web 服務(wù)器發(fā)送一個 HTTP 請求

          拿到域名對應(yīng)的IP地址之后,瀏覽器會以一個隨機(jī)端口(1024<端口<65535)向服務(wù)器的WEB程序(常用的有httpd,nginx等)80端口發(fā)起TCP的連接請求。這個連接請求到達(dá)服務(wù)器端后(這中間通過各種路由設(shè)備,局域網(wǎng)內(nèi)除外),進(jìn)入到網(wǎng)卡,然后是進(jìn)入到內(nèi)核的TCP/IP協(xié)議棧(用于識別該連接請求,解封包,一層一層的剝開),還有可能要經(jīng)過Netfilter防火墻(屬于內(nèi)核的模塊)的過濾,最終到達(dá)WEB程序,最終建立了TCP/IP的連接。
            
          TCP連接如圖所示:   

          建立了TCP連接之后,發(fā)起一個http請求。一個典型的 http request header 一般需要包括請求的方法,例如 GET 或者 POST 等,不常用的還有 PUT 和 DELETE 、HEAD、OPTION以及 TRACE 方法,一般的瀏覽器只能發(fā)起 GET 或者 POST 請求。
            
          客戶端向服務(wù)器發(fā)起http請求的時候,會有一些請求信息,請求信息包含三個部分:

          請求方法URI協(xié)議/版本

          請求頭(Request Header)

          請求正文

          下面是一個完整的HTTP請求例子:

          GET/sample.jspHTTP/1.1
          Accept:image/gif.image/jpeg,*/*
          Accept-Language:zh-cn
          Connection:Keep-Alive
          Host:localhost
          User-Agent:Mozila/4.0(compatible;MSIE5.01;Window?NT5.0)
          Accept-Encoding:gzip,deflate
          
          username=jinqiao&password=1234
          

          注意:最后一個請求頭之后是一個空行,發(fā)送回車符和換行符,通知服務(wù)器以下不再有請求頭。

          1.請求的第一行是“方法URL議/版本”:GET/sample.jsp HTTP/1.1
          2.請求頭(Request Header)

          請求頭包含許多有關(guān)的客戶端環(huán)境和請求正文的有用信息。例如,請求頭可以聲明瀏覽器所用的語言,請求正文的長度等。

          Accept:image/gif.image/jpeg.*/*
          Accept-Language:zh-cn
          Connection:Keep-Alive
          Host:localhost
          User-Agent:Mozila/4.0(compatible:MSIE5.01:Windows?NT5.0)
          Accept-Encoding:gzip,deflate.
          

          3.請求正文
          請求頭和請求正文之間是一個空行,這個行非常重要,它表示請求頭已經(jīng)結(jié)束,接下來的是請求正文。請求正文中可以包含客戶提交的查詢字符串信息:

          username=jinqiao&password=1234
          

          — 知識擴(kuò)展—

          1.TCP三次握手

          第一次握手:客戶端A將標(biāo)志位SYN置為1,隨機(jī)產(chǎn)生一個值為seq=J(J的取值范圍為=1234567)的數(shù)據(jù)包到服務(wù)器,客戶端A進(jìn)入SYN_SENT狀態(tài),等待服務(wù)端B確認(rèn);

          第二次握手:服務(wù)端B收到數(shù)據(jù)包后由標(biāo)志位SYN=1知道客戶端A請求建立連接,服務(wù)端B將標(biāo)志位SYN和ACK都置為1,ack=J+1,隨機(jī)產(chǎn)生一個值seq=K,并將該數(shù)據(jù)包發(fā)送給客戶端A以確認(rèn)連接請求,服務(wù)端B進(jìn)入SYN_RCVD狀態(tài)。

          第三次握手:客戶端A收到確認(rèn)后,檢查ack是否為J+1,ACK是否為1,如果正確則將標(biāo)志位ACK置為1,ack=K+1,并將該數(shù)據(jù)包發(fā)送給服務(wù)端B,服務(wù)端B檢查ack是否為K+1,ACK是否為1,如果正確則連接建立成功,客戶端A和服務(wù)端B進(jìn)入ESTABLISHED狀態(tài),完成三次握手,隨后客戶端A與服務(wù)端B之間可以開始傳輸數(shù)據(jù)了。

          如圖所示:

          2.為什需要三次握手?

          《計(jì)算機(jī)網(wǎng)絡(luò)》第四版中講“三次握手”的目的是“為了防止已失效的連接請求報文段突然又傳送到了服務(wù)端,因而產(chǎn)生錯誤”

          書中的例子是這樣的,“已失效的連接請求報文段”的產(chǎn)生在這樣一種情況下:client發(fā)出的第一個連接請求報文段并沒有丟失,而是在某個網(wǎng)絡(luò)結(jié)點(diǎn)長時間的滯留了,以致延誤到連接釋放以后的某個時間才到達(dá)server。本來這是一個早已失效的報文段。但server收到此失效的連接請求報文段后,就誤認(rèn)為是client再次發(fā)出的一個新的連接請求。于是就向client發(fā)出確認(rèn)報文段,同意建立連接。

          假設(shè)不采用“三次握手”,那么只要server發(fā)出確認(rèn),新的連接就建立了。由于現(xiàn)在client并沒有發(fā)出建立連接的請求,因此不會理睬server的確認(rèn),也不會向server發(fā)送數(shù)據(jù)。但server卻以為新的運(yùn)輸連接已經(jīng)建立,并一直等待client發(fā)來數(shù)據(jù)。這樣,server的很多資源就白白浪費(fèi)掉了。采用“三次握手”的辦法可以防止上述現(xiàn)象發(fā)生。例如剛才那種情況,client不會向server的確認(rèn)發(fā)出確認(rèn)。server由于收不到確認(rèn),就知道client并沒有要求建立連接。”。主要目的防止server端一直等待,浪費(fèi)資源。

          3.TCP四次揮手

          第一次揮手:Client發(fā)送一個FIN,用來關(guān)閉Client到Server的數(shù)據(jù)傳送,Client進(jìn)入FIN_WAIT_1狀態(tài)。

          第二次揮手:Server收到FIN后,發(fā)送一個ACK給Client,確認(rèn)序號為收到序號+1(與- SYN相同,一個FIN占用一個序號),Server進(jìn)入CLOSE_WAIT狀態(tài)。

          第三次揮手:Server發(fā)送一個FIN,用來關(guān)閉Server到Client的數(shù)據(jù)傳送,Server進(jìn)入LAST_ACK狀態(tài)。

          第四次揮手:Client收到FIN后,Client進(jìn)入TIME_WAIT狀態(tài),接著發(fā)送一個ACK給Server,確認(rèn)序號為收到序號+1,Server進(jìn)入CLOSED狀態(tài),完成四次揮手。

          4.為什么建立連接是三次握手,而關(guān)閉連接卻是四次揮手呢?

          這是因?yàn)榉?wù)端在LISTEN狀態(tài)下,收到建立連接請求的SYN報文后,把ACK和SYN放在一個報文里發(fā)送給客戶端。而關(guān)閉連接時,當(dāng)收到對方的FIN報文時,僅僅表示對方不再發(fā)送數(shù)據(jù)了但是還能接收數(shù)據(jù),己方也未必全部數(shù)據(jù)都發(fā)送給對方了,所以己方可以立即close,也可以發(fā)送一些數(shù)據(jù)給對方后,再發(fā)送FIN報文給對方來表示同意現(xiàn)在關(guān)閉連接,因此,己方ACK和FIN一般都會分開發(fā)送。


          4、服務(wù)器的永久重定向響應(yīng)

          服務(wù)器給瀏覽器響應(yīng)一個301永久重定向響應(yīng),這樣瀏覽器就會訪問http://www.google.com/而非http://google.com/。

          為什么服務(wù)器一定要重定向而不是直接發(fā)送用戶想看的網(wǎng)頁內(nèi)容呢?其中一個原因跟搜索引擎排名有關(guān)。如果一個頁面有兩個地址,就像http://www.yy.com/和http://yy.com/,搜索引擎會認(rèn)為它們是兩個網(wǎng)站,結(jié)果造成每個搜索鏈接都減少從而降低排名。而搜索引擎知道301永久重定向是什么意思,這樣就會把訪問帶www的和不帶www的地址歸到同一個網(wǎng)站排名下。還有就是用不同的地址會造成緩存友好性變差,當(dāng)一個頁面有好幾個名字時,它可能會在緩存里出現(xiàn)好幾次。

          —-擴(kuò)展知識—-

          1.301和302的區(qū)別。

          301和302狀態(tài)碼都表示重定向,就是說瀏覽器在拿到服務(wù)器返回的這個狀態(tài)碼后會自動跳轉(zhuǎn)到一個新的URL地址,這個地址可以從響應(yīng)的Location首部中獲取(用戶看到的效果就是他輸入的地址A瞬間變成了另一個地址B)——這是它們的共同點(diǎn)。

          他們的不同在于。301表示舊地址A的資源已經(jīng)被永久地移除了(這個資源不可訪問了),搜索引擎在抓取新內(nèi)容的同時也將舊的網(wǎng)址交換為重定向之后的網(wǎng)址;

          302表示舊地址A的資源還在(仍然可以訪問),這個重定向只是臨時地從舊地址A跳轉(zhuǎn)到地址B,搜索引擎會抓取新的內(nèi)容而保存舊的網(wǎng)址。SEO302好于301

          2.重定向原因:

          網(wǎng)站調(diào)整(如改變網(wǎng)頁目錄結(jié)構(gòu));

          網(wǎng)頁被移到一個新地址;

          網(wǎng)頁擴(kuò)展名改變(如應(yīng)用需要把.php改成.Html或.shtml)。

          這種情況下,如果不做重定向,則用戶收藏夾或搜索引擎數(shù)據(jù)庫中舊地址只能讓訪問客戶得到一個404頁面錯誤信息,訪問流量白白喪失;再者某些注冊了多個域名的網(wǎng)站,也需要通過重定向讓訪問這些域名的用戶自動跳轉(zhuǎn)到主站點(diǎn)等。

          3.什么時候進(jìn)行301或者302跳轉(zhuǎn)呢?

          當(dāng)一個網(wǎng)站或者網(wǎng)頁24—48小時內(nèi)臨時移動到一個新的位置,這時候就要進(jìn)行302跳轉(zhuǎn),而使用301跳轉(zhuǎn)的場景就是之前的網(wǎng)站因?yàn)槟撤N原因需要移除掉,然后要到新的地址訪問,是永久性的。

          清晰明確而言:使用301跳轉(zhuǎn)的大概場景如下:

          域名到期不想續(xù)費(fèi)(或者發(fā)現(xiàn)了更適合網(wǎng)站的域名),想換個域名。

          在搜索引擎的搜索結(jié)果中出現(xiàn)了不帶www的域名,而帶www的域名卻沒有收錄,這個時候可以用301重定向來告訴搜索引擎我們目標(biāo)的域名是哪一個。

          空間服務(wù)器不穩(wěn)定,換空間的時候。

          5、瀏覽器跟蹤重定向地址

          現(xiàn)在瀏覽器知道了 http://www.google.com/ 才是要訪問的正確地址,所以它會發(fā)送另一個http請求。


          6、服務(wù)器處理請求

          經(jīng)過前面的重重步驟,我們終于將我們的http請求發(fā)送到了服務(wù)器這里,其實(shí)前面的重定向已經(jīng)是到達(dá)服務(wù)器了,那么,服務(wù)器是如何處理我們的請求的呢?

          后端從在固定的端口接收到TCP報文開始,它會對TCP連接進(jìn)行處理,對HTTP協(xié)議進(jìn)行解析,并按照報文格式進(jìn)一步封裝成HTTP Request對象,供上層使用。

          一些大一點(diǎn)的網(wǎng)站會將你的請求到反向代理服務(wù)器中,因?yàn)楫?dāng)網(wǎng)站訪問量非常大,網(wǎng)站越來越慢,一臺服務(wù)器已經(jīng)不夠用了。于是將同一個應(yīng)用部署在多臺服務(wù)器上,將大量用戶的請求分配給多臺機(jī)器處理。

          此時,客戶端不是直接通過HTTP協(xié)議訪問某網(wǎng)站應(yīng)用服務(wù)器,而是先請求到Nginx,Nginx再請求應(yīng)用服務(wù)器,然后將結(jié)果返回給客戶端,這里Nginx的作用是反向代理服務(wù)器。同時也帶來了一個好處,其中一臺服務(wù)器萬一掛了,只要還有其他服務(wù)器正常運(yùn)行,就不會影響用戶使用。

          如圖所示:

          通過Nginx的反向代理,我們到達(dá)了web服務(wù)器,服務(wù)端腳本處理我們的請求,訪問我們的數(shù)據(jù)庫,獲取需要獲取的內(nèi)容等等,當(dāng)然,這個過程涉及很多后端腳本的復(fù)雜操作。由于對這一塊不熟,所以這一塊只能介紹這么多了。

          —-擴(kuò)展閱讀—-

          1.什么是反向代理?

          客戶端本來可以直接通過HTTP協(xié)議訪問某網(wǎng)站應(yīng)用服務(wù)器,網(wǎng)站管理員可以在中間加上一個Nginx,客戶端請求Nginx,Nginx請求應(yīng)用服務(wù)器,然后將結(jié)果返回給客戶端,此時Nginx就是反向代理服務(wù)器。


          7、服務(wù)器返回一個 HTTP 響應(yīng) 

          經(jīng)過前面的6個步驟,服務(wù)器收到了我們的請求,也處理我們的請求,到這一步,它會把它的處理結(jié)果返回,也就是返回一個HTPP響應(yīng)。

          HTTP響應(yīng)與HTTP請求相似,HTTP響應(yīng)也由3個部分構(gòu)成,分別是:

          狀態(tài)行

          響應(yīng)頭(Response Header)

          響應(yīng)正文


          HTTP/1.1?200?OK
          Date:?Sat,?31?Dec?2005?23:59:59?GMT
          Content-Type:?text/html;charset=ISO-8859-1
          Content-Length:?122
          
          <html>
          <head>
          <title>http</title>
          </head>
          <body>
          <!--?body?goes?here?-->
          </body>
          </html>
          

          狀態(tài)行:

          狀態(tài)行由協(xié)議版本、數(shù)字形式的狀態(tài)代碼、及相應(yīng)的狀態(tài)描述,各元素之間以空格分隔。

          格式: HTTP-Version Status-Code Reason-Phrase CRLF
          例如: HTTP/1.1 200 OK

          協(xié)議版本:是用http1.0還是其他版本

          狀態(tài)描述:狀態(tài)描述給出了關(guān)于狀態(tài)代碼的簡短的文字描述。比如狀態(tài)代碼為200時的描述為 ok

          狀態(tài)碼:狀態(tài)代碼由三位數(shù)字組成,第一個數(shù)字定義了響應(yīng)的類別,且有五種可能取值,如下:

          1xx:信息性狀態(tài)碼,表示服務(wù)器已接收了客戶端請求,客戶端可繼續(xù)發(fā)送請求。

          100 Continue

          101 Switching Protocols

          2xx:成功狀態(tài)碼,表示服務(wù)器已成功接收到請求并進(jìn)行處理。

          200 OK 表示客戶端請求成功

          204 No Content 成功,但不返回任何實(shí)體的主體部分

          206 Partial Content 成功執(zhí)行了一個范圍(Range)請求

          3xx:重定向狀態(tài)碼,表示服務(wù)器要求客戶端重定向。

          301 Moved Permanently 永久性重定向,響應(yīng)報文的Location首部應(yīng)該有該資源的新URL

          302 Found 臨時性重定向,響應(yīng)報文的Location首部給出的URL用來臨時定位資源

          303 See Other 請求的資源存在著另一個URI,客戶端應(yīng)使用GET方法定向獲取請求的資源

          304 Not Modified 服務(wù)器內(nèi)容沒有更新,可以直接讀取瀏覽器緩存

          307 Temporary Redirect 臨時重定向。與302 Found含義一樣。302禁止POST變換為GET,但實(shí)際使用時并不一定,307則更多瀏覽器可能會遵循這一標(biāo)準(zhǔn),但也依賴于瀏覽器具體實(shí)現(xiàn)

          4xx:客戶端錯誤狀態(tài)碼,表示客戶端的請求有非法內(nèi)容。

          400 Bad Request 表示客戶端請求有語法錯誤,不能被服務(wù)器所理解

          401 Unauthonzed 表示請求未經(jīng)授權(quán),該狀態(tài)代碼必須與 WWW-Authenticate 報頭域一起使用

          403 Forbidden 表示服務(wù)器收到請求,但是拒絕提供服務(wù),通常會在響應(yīng)正文中給出不提供服務(wù)的原因

          404 Not Found 請求的資源不存在,例如,輸入了錯誤的URL

          5xx:服務(wù)器錯誤狀態(tài)碼,表示服務(wù)器未能正常處理客戶端的請求而出現(xiàn)意外錯誤。

          500 Internel Server Error 表示服務(wù)器發(fā)生不可預(yù)期的錯誤,導(dǎo)致無法完成客戶端的請求

          503 Service Unavailable 表示服務(wù)器當(dāng)前不能夠處理客戶端的請求,在一段時間之后,服務(wù)器可能會恢復(fù)正常

          響應(yīng)頭:

          響應(yīng)頭部:由關(guān)鍵字/值對組成,每行一對,關(guān)鍵字和值用英文冒號”:”分隔,典型的響應(yīng)頭有:

          響應(yīng)正文

          包含著我們需要的一些具體信息,比如cookie,html,image,后端返回的請求數(shù)據(jù)等等。這里需要注意,響應(yīng)正文和響應(yīng)頭之間有一行空格,表示響應(yīng)頭的信息到空格為止,下圖是fiddler抓到的請求正文,紅色框中的:響應(yīng)正文:


          8、瀏覽器顯示 HTML

          在瀏覽器沒有完整接受全部HTML文檔時,它就已經(jīng)開始顯示這個頁面了,瀏覽器是如何把頁面呈現(xiàn)在屏幕上的呢?不同瀏覽器可能解析的過程不太一樣,這里我們只介紹webkit的渲染過程,下圖對應(yīng)的就是WebKit渲染的過程,這個過程包括:

          解析html以構(gòu)建dom樹 -> 構(gòu)建render樹 -> 布局render樹 -> 繪制render樹

          瀏覽器在解析html文件時,會”自上而下“加載,并在加載過程中進(jìn)行解析渲染。在解析過程中,如果遇到請求外部資源時,如圖片、外鏈的CSS、iconfont等,請求過程是異步的,并不會影響html文檔進(jìn)行加載。

          解析過程中,瀏覽器首先會解析HTML文件構(gòu)建DOM樹,然后解析CSS文件構(gòu)建渲染樹,等到渲染樹構(gòu)建完成后,瀏覽器開始布局渲染樹并將其繪制到屏幕上。這個過程比較復(fù)雜,涉及到兩個概念: reflow(回流)和repain(重繪)。

          DOM節(jié)點(diǎn)中的各個元素都是以盒模型的形式存在,這些都需要瀏覽器去計(jì)算其位置和大小等,這個過程稱為relow;當(dāng)盒模型的位置,大小以及其他屬性,如顏色,字體,等確定下來之后,瀏覽器便開始繪制內(nèi)容,這個過程稱為repain。

          頁面在首次加載時必然會經(jīng)歷reflow和repain。reflow和repain過程是非常消耗性能的,尤其是在移動設(shè)備上,它會破壞用戶體驗(yàn),有時會造成頁面卡頓。所以我們應(yīng)該盡可能少的減少reflow和repain

          當(dāng)文檔加載過程中遇到j(luò)s文件,html文檔會掛起渲染(加載解析渲染同步)的線程,不僅要等待文檔中js文件加載完畢,還要等待解析執(zhí)行完畢,才可以恢復(fù)html文檔的渲染線程。因?yàn)镴S有可能會修改DOM,最為經(jīng)典的document.write,這意味著,在JS執(zhí)行完成前,后續(xù)所有資源的下載可能是沒有必要的,這是js阻塞后續(xù)資源下載的根本原因。所以我明平時的代碼中,js是放在html文檔末尾的。

          JS的解析是由瀏覽器中的JS解析引擎完成的,比如谷歌的是V8。JS是單線程運(yùn)行,也就是說,在同一個時間內(nèi)只能做一件事,所有的任務(wù)都需要排隊(duì),前一個任務(wù)結(jié)束,后一個任務(wù)才能開始。但是又存在某些任務(wù)比較耗時,如IO讀寫等,所以需要一種機(jī)制可以先執(zhí)行排在后面的任務(wù),這就是:同步任務(wù)(synchronous)和異步任務(wù)(asynchronous)。

          JS的執(zhí)行機(jī)制就可以看做是一個主線程加上一個任務(wù)隊(duì)列(task queue)。同步任務(wù)就是放在主線程上執(zhí)行的任務(wù),異步任務(wù)是放在任務(wù)隊(duì)列中的任務(wù)。所有的同步任務(wù)在主線程上執(zhí)行,形成一個執(zhí)行棧;異步任務(wù)有了運(yùn)行結(jié)果就會在任務(wù)隊(duì)列中放置一個事件;腳本運(yùn)行時先依次運(yùn)行執(zhí)行棧,然后會從任務(wù)隊(duì)列里提取事件,運(yùn)行任務(wù)隊(duì)列中的任務(wù),這個過程是不斷重復(fù)的,所以又叫做事件循環(huán)(Event loop)。具體的過程可以看這篇文章:

          http://www.cnblogs.com/xianyulaodi/p/6414805.html


          9、瀏覽器發(fā)送請求獲取嵌入在 HTML 中的資源(如圖片、音頻、視頻、CSS、JS等等)

          其實(shí)這個步驟可以并列在步驟8中,在瀏覽器顯示HTML時,它會注意到需要獲取其他地址內(nèi)容的標(biāo)簽。這時,瀏覽器會發(fā)送一個獲取請求來重新獲得這些文件。比如我要獲取外圖片,CSS,JS文件等,類似于下面的鏈接:

          圖片:http://static.ak.fbcdn.net/rsrc.php/z12E0/hash/8q2anwu7.gif
          CSS式樣表:http://static.ak.fbcdn.net/rsrc.php/z448Z/hash/2plh8s4n.css
          JavaScript 文件:http://static.ak.fbcdn.net/rsrc.php/zEMOA/hash/c8yzb6ub.js

          這些地址都要經(jīng)歷一個和HTML讀取類似的過程。所以瀏覽器會在DNS中查找這些域名,發(fā)送請求,重定向等等…

          不像動態(tài)頁面,靜態(tài)文件會允許瀏覽器對其進(jìn)行緩存。有的文件可能會不需要與服務(wù)器通訊,而從緩存中直接讀取,或者可以放到CDN中


          主站蜘蛛池模板: 国产精品无码亚洲一区二区三区 | 精品在线视频一区| tom影院亚洲国产一区二区| 亚洲丰满熟女一区二区哦| 中文字幕在线播放一区| 成人免费一区二区无码视频| 久久精品道一区二区三区| 国产一区二区三区免费观在线| 亚洲一区中文字幕在线电影网| 日本不卡一区二区视频a| 福利视频一区二区牛牛| 精品国产一区二区麻豆| 亚洲国产AV一区二区三区四区| 日韩一区二区三区无码影院| 亚洲宅男精品一区在线观看| 美女视频一区二区| 国产精品538一区二区在线| 中文字幕av一区| 蜜桃传媒一区二区亚洲AV| 国产高清一区二区三区四区| 国产萌白酱在线一区二区| 精品日韩一区二区| 国产一区二区影院| 国产大秀视频一区二区三区 | 精品国产精品久久一区免费式| 亚洲一区精彩视频| 久久人妻无码一区二区| 色屁屁一区二区三区视频国产| 在线免费视频一区二区| 精品不卡一区中文字幕| 亚洲视频一区二区| 亚洲国产精品无码久久一区二区| 精品福利一区二区三| 久久无码AV一区二区三区| 91精品国产一区二区三区左线 | 亚洲国产精品第一区二区| 日韩一区二区在线观看视频| 中文字幕日韩一区二区不卡| 色屁屁一区二区三区视频国产| 无码精品人妻一区| 无码AV天堂一区二区三区|