.1、什么是HTML語義化?有什么好處?
根據內容的結構化(內容語義化),選擇合適的標簽(代碼語義化)便于開發者閱讀和寫出更優雅的代碼的同時讓瀏覽器的爬蟲和機器很好地解析。 為了在沒有 CSS 的情況下,頁面也能呈現出很好地內容結構、代碼結構:為了裸奔時好看; 用戶體驗:例如 title、alt 用于解釋名詞或解釋圖片信息、label標簽的活用; 有利于 SEO:和搜索引擎建立良好溝通,有助于爬蟲抓取更多的有效信息:爬蟲依賴于標簽來確定上下文和各個關鍵字的權重; 方便其他設備解析(如屏幕閱讀器、盲人閱讀器、移動設備)以語義的方式來渲染網頁; 便于團隊開發和維護,語義化更具可讀性,是下一步網頁的重要動向,遵循這個標準,可以減少差異化。
1.2、html5有哪些新特性?
HTML5 已經不是 SGML 的子集 新增關于圖像,地理位置,緩存,多任務等多個功能 用于媒體回放的 Video 和 Audio 語義化更好的標簽如 header、footer、section、aside、nav、canvas、time等等,利于SEO優化 新增的表單元素如 <datalist>、<keygen>、<output> 新增的表單屬性如 autocomplete、autofocus、placeholder、required、step等等。 HTML5 Web 存儲 sessionStorage 和 localStorage HTML5 WebSocket HTML5 離線Web應用(應用程序緩存)
1.3、你能解釋一下CSS的盒子模型么?
CSS盒模型是圍繞在HTML元素周圍的定義 Border(邊界),padding(內邊距)和
margin(外邊距)的矩形空間
Border(邊界):定義了元素包含的最大區域
Padding(內邊距):定義了邊界和內部元素的間距
Margin:定義了邊界和任何相鄰元素的間距
1.4、CSS選擇器有哪些?哪些屬性可以繼承?
CSS選擇符:id選擇器(#myid)、類選擇器(.myclassname)、標簽選擇器(div, h1, p)、相鄰選擇器(h1 + p)、子選擇器(ul > li)、后代選擇器(li a)、通配符選擇器(*)、屬性選擇器(a[rel=”external”])、偽類選擇器(a:hover, li:nth-child) 可繼承的屬性:font-size, font-family, color 不可繼承的樣式:border, padding, margin, width, height 優先級(就近原則):!important > [ id > class > tag ] !important 比內聯優先級高
1.5、CSS3新增偽類有那些?
E:nth-last-child(n) E:nth-of-type(n) E:nth-last-of-type(n) E:last-child E:first-of-type E:only-child E:only-of-type E:empty E:checked E:enabled E:disabled E::selection E:not(s) E::not(.s) tbody: nth-child(even), nth-child(odd)/*:此處他們分別代表了表格(tbody)下面的偶數行和奇數行(tr)*/等等......
1.6、css3有那些新特性?
RGBA 和透明度
background-image background-origin(content-box/padding-box/border-box)
background-size background-repeat
word-wrap(對長的不可分割單詞換行)word-wrap:break-word
文字陰影:text-shadow: 5px 5px 5px #FF0000;(水平陰影,垂直陰影,模糊距離,陰影顏色)
font-face屬性:定義自己的字體
圓角(邊框半徑):border-radius 屬性用于創建圓角
邊框圖片:border-image: url(border.png) 30 30 round
盒陰影:box-shadow: 10px 10px 5px #888888
媒體查詢:定義兩套css,當瀏覽器的尺寸變化時會采用不同的屬性
1.7、對BFC規范(塊級格式化上下文:block formatting context)的理解?
BFC規定了內部的 Block Box 如何布局。
定位方案:
內部的 Box 會在垂直方向上一個接一個放置。
Box 垂直方向的距離由 margin 決定,屬于同一個BFC的兩個相鄰Box的margin會發生重疊。
每個元素的margin box 的左邊,與包含塊border box的左邊相接觸。
BFC的區域不會與float box重疊。
BFC是頁面上的一個隔離的獨立容器,容器里面的子元素不會影響到外面的元素。
計算BFC的高度時,浮動元素也會參與計算。
滿足下列條件之一就可觸發BFC:
根元素,即html
float 的值不為 none(默認)
overflow 的值不為 visible(默認)
display 的值為 inline-block、table-cell、table-caption
position 的值為 absolute 或 fixed
1.8、 CSS優化、提高性能的方法有哪些?
避免過度約束 避免后代選擇符 避免鏈式選擇符 使用緊湊的語法 避免不必要的命名空間 避免不必要的重復 最好使用表示語義的名字。一個好的類名應該是描述他是什么而不是像什么 避免!important,可以選擇其他選擇器 盡可能的精簡規則,你可以合并不同類里的重復規則
1.9、兩個div并排,左邊div固定寬度,右邊寬度自適應”,至少列出4種。
方式一:BFC(塊級格式化上下文) 思路:左邊定寬 float:left,右邊采用 overflow: hidden; /* 觸發bfc */ 方式二:采用flex布局 這種應該是最簡單的方式,右邊 flex:1 方式三:采用 display:table的方式來實現布局 父元素 display:table,兩個子元素采用 display:table-cell; 方式四:采用calc計算寬度的方式來實現 方式五:采用absolute+margin-left來實現
1.10、Sass和Less的異同之處。
1、Less 環境較 Sass簡單 Cass的安裝需要安裝Ruby環境,Less基于JavaScript,是需要引入Less.js來處理代碼輸出css到瀏覽器,也可以在開發環節使用Less,然后編譯成css文件,直接放在項目中,有less.app、SimpleLess、CodeKit.app這樣的工具,也有在線編輯地址。 2、Less 使用較 Sass簡單 LESS 并沒有裁剪 CSS 原有的特性,而是在現有 CSS 語法的基礎上,為 CSS 加入程序式語言的特性。只要你了解 CSS 基礎就可以很容易上手。 3、從功能出發,Sass 較 Less 略強大一些 ①sass有變量和作用域。 - $variable,like php; - #{$variable}like ruby; - 變量有全局和局部之分,并且有優先級。 ② sass 有函數的概念; - @function和@return以及函數參數(還有不定參)可以讓你像js開發那樣封裝你想要的邏輯。 -@mixin類似function但缺少像function的編程邏輯,更多的是提高css代碼段的復用性和模塊化,這個用的人也是最多的。 -ruby提供了非常豐富的內置原生api。 ③進程控制: -條件:@if @else; -循環遍歷:@for @each @while -繼承:@extend -引用:@import ④數據結構: -$list類型=數組; -$map類型=object; 其余的也有string、number、function等類型 4、Less 與Sass 處理機制不一樣 前者是通過客戶端處理的,后者是通過服務端處理,相比較之下前者解析會比后者慢一點 5、關于變量在 Less 和 Sass 中的唯一區別就是 Less 用@,Sass 用$。
###二、Javascript 部分
2.1、介紹JavaScript的基本數據類型
String、Number、Boolean、Null、Undefined、Symbol、BigInt
2.2談談this的理解
this 總是指向函數的直接調用者(而非間接調用者) 如果有new關鍵字,this 指向new 出來的那個對象 在事件中,this 指向目標元素,特殊的是IE的attachEvent中的this總是指向全局對象window。
2.3、null,undefined的區別?
null表示一個對象被定義了,但存放了空指針,轉換為數值時為0。 undefined表示聲明的變量未初始化,轉換為數值時為NAN。 typeof(null) -- object; typeof(undefined) -- undefined
2.4、 什么是閉包(closure),為什么要用它?
閉包指的是一個函數可以訪問另一個函數作用域中變量。常見的構造方法,是在一個函數內部定義另外一個函數。內部函數可以引用外層的變量;外層變量不會被垃圾回收機制回收。 注意,閉包的原理是作用域鏈,所以閉包訪問的上級作用域中的變量是個對象,其值為其運算結束后的最后一個值。 優點:避免全局變量污染。缺點:容易造成內存泄漏。 特性: a. JavaScript允許你使用在當前函數以外定義的變量 b. 即使外部函數已經返回,當前函數仍然可以引用在外部函數所定義的變量 c. 閉包可以更新外部變量的值 d. 用閉包模擬私有方法 由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題 例子: function makeFunc() { var name="Mozilla"; function displayName() { console.log(name); } return displayName; } var myFunc=makeFunc(); myFunc(); //輸出Mozilla
2.5、如何判斷一個對象是否屬于某個類?
使用instanceof 即if(a instanceof Person){alert('yes');}
2.6、new操作符具體干了什么呢?
創建一個空對象,并且 this 變量引用該對象,同時還繼承了該函數的原型。 屬性和方法被加入到 this 引用的對象中。 新創建的對象由 this 所引用,并且最后隱式的返回 this 。
2.6、JS延遲加載的方式有哪些?
JS的延遲加載有助與提高頁面的加載速度。 defer和async、動態創建DOM方式(用得最多)、按需異步載入JS defer:延遲腳本。立即下載,但延遲執行(延遲到整個頁面都解析完畢后再運行),按照腳本出現的先后順序執行。 async:異步腳本。下載完立即執行,但不保證按照腳本出現的先后順序執行。
2.7、call和apply的區別
call()方法和apply()方法的作用相同,動態改變某個類的某個方法的運行環境。他們的區別在于接收參數的方式不同。在使用call(obj,a1,a2...)方法時,傳遞給函數的參數必須逐個列舉出來。使用apply(obj,[a1,a2...])時,傳遞給函數的是參數數組。
2.8、數組對象有哪些原生方法,列舉一下
pop、push、shift、unshift、splice、reverse、sort、concat、join、slice、toString、indexOf、lastIndexOf、reduce、reduceRight、forEach、map、filter、every、some
2.9、什么是跨域?如何解決?
要明白什么是跨域之前,首先要明白什么是同源策略? 同源策略就是用來限制從一個源加載的文檔或腳本與來自另一個源的資源進行交互。那怎樣判斷是否是同源呢? 如果協議,端口(如果指定了)和主機對于兩個頁面是相同的,則兩個頁面具有相同的源,也就是同源。也就是說,要同時滿足以下3個條件,才能叫同源: 協議相同 端口相同 主機相同 解決方案: 1.iframe 隨著近年來前端技術的飛躍發展以及移動互聯網時代的洗禮,iframe的使用漸漸的不被建議,雖然也是一種跨域請求的解決方案,但這里就不再講述,請讀者自行查閱網上資料。 2.jsonp jsonp是比較常用的方法,我們假設a.com域名需要向b.com發起一個api請求(jsonp的一個缺點是,僅能接受GET方式),則使用JSONP完成該過程的。 3. 通過請求同域下的api,間接獲取它域的數據 我們仍以域名a.com/demo.html需獲取b.com下的數據為例,這時候只要在a.com下創建一個demo.php,由demo.php通過curl的方式向b.com發起數據請求,并包裝請求結果返回給a.com/demo.html頁面。這里主要是通過與a.com/demo.html同域下的a.com/demo.php做了一層數據請求代理,避免了前端跨域請求。 4.使用web服務器的反向代理設置 同樣是使用代理的思維,但與2不同的是,我們這里使用web服務器的反向代理配置: Nginx反向代理可以使用 proxy_pass Apache2的反向代理的配置可以使用ProxyPass 5.設置header頭(CORS) 在你要跨域請求的api里,設置header頭Access-Control-Allow-Origin: "*";
2.10、如何實現js中的繼承
1、原型繼承的第一種方式:
function Cat(name){
this.name=name;
}
//原型繼承
Cat.prototype.say=function(){
alert("你好,我是一只貓,我叫:"+this.name);
}
2、原型繼承第二種方式:
function Cat(name) {
this.name=name;
}
function Animal() {}
Animal.prototype.run=function () {
alert("動物跑");
};
Cat.prototype=new Animal();
Cat.prototype.constructor=Cat;
3、借用構造函數
function Cat(name,age) {
Animal.call(this,name,age);
}
function Animal(name,age) {
this.name=name;
this.age=age;
}
4、經典繼承
function create(obj) {
if(Object.create) {
return Object.create(obj);
} else {
function F(){};
F.prototype=obj;
return new F();
}
}
2.11、看下列代碼,輸出什么?解釋原因。
var undefined;//此時undefined這個變量的值是undefined undefined==null; // true 1==true; // true 此時會把布爾類型的值轉換為數字類型 true=1 false=0 2==true; // false 0==false; // true 0==''; // true NaN==NaN; // false isNaN []==false; // true 解釋:會把[]和false都通過Number()轉換為數字類型 []==![]; // true 解釋:![]:false []==[];//false 一個是number一個是string時,會嘗試將string轉換為number 一個是number一個是boolean,將boolean轉換為number,結果:true:1 false:0 一個是object 另一個是string或number,將Object轉換成number或string 所以,對于0、空字符串的判斷,建議使用 “===” ?!?==”會先判斷兩邊的值類型,類型不匹配時為false。
2.12、已知數組numberArray=[3,6,2,4,1,5];
1) 實現對該數組的倒排,輸出[5,1,4,2,6,3] function reverseArray(arr){ var result=[]; //方法1: /*for (var i=arr.length - 1; i >=0; i--) { result.push(arr[i]); }*/ //方法2: for (var i=0, len=arr.length; i < len; i++) { result.unshift(arr[i]); } return result; } 2) 實現對該數組的降序排列,輸出[6,5,4,3,2,1] 冒泡排序過程演示 function sortDesc(arr) { for (var i=0, len=arr.length; i < len; i++) { for (var j=i + 1, len2=arr.length; j < len2; j++) { //>就是降序 <就是升序 if (arr[j] > arr[i]) { var temp=arr[j]; arr[j]=arr[i]; arr[i]=temp; } } } return arr; }
2.13、有這樣一個URL:http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e,請寫一段JS程序提取URL中的各個GET參數(參數名和參數個數不確定),將其按key-value形式返回到一個json結構中,如{a:’1′, b:’2′, c:”, d:’xxx’, e:undefined}。
答案: function serlize(url){ var result={}; //1、尋找?后面的字符串 url=url.substr(url.indexOf("?")+1); //2、將字符串用&分隔 var args=url.split("&");//[“a=1”,”b=2”] for (var i=0, len=args.length; i < len; i++) { var arg=args[i]; var item=arg.split('='); //3、對象的鍵=值 result[item[0]]=item[1]; } return result; } serlize(‘http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e‘);
2.14、什么是 Javascript 高階函數?并寫出例子
如果一個函數操作其他函數,即將其他函數作為參數或將函數作為返回值,則稱這個函數是一個高階函數。
我們來看看下面的例子:
function abc(temp){
console.log(temp);
}
function def(temp1,temp2){
temp1(temp2);
}
def(abc,"sos");
執行之后,輸出:sos
首先我們定義了兩個函數 abc() 和 def() ,
然后執行 def(abc,"sos"),我們把abc 這個函數作為了函數def() 的一個參數,
最后在函數def 中執行了 abc() 這個函數;
2.15、JavaScript 什么是原型鏈?
原型鏈 : 實例對象與原型之間的鏈接,叫做原型鏈 Function.prototype.a="a"; Object.prototype.b="b"; function Person(){} console.log(Person); //function Person() let p=new Person(); console.log(p); //Person {} 對象 console.log(p.a); //undefined console.log(p.b); //b 想一想p.a打印結果為undefined,p.b結果為b 解析: p是Person()的實例,是一個Person對象,它擁有一個屬性值__proto__,并且__proto__是一個對象,包含兩個屬性值constructor和__proto__。 console.log(p.__proto__.constructor); //function Person(){} console.log(p.__proto__.__proto__); //對象{},擁有很多屬性值 我們會發現p.__proto__.constructor返回的結果為構造函數本身, p.__proto__.__proto__有很多參數
我們調用constructor屬性,p.___proto__.__proto__.constructor得到擁有多個參數的Object()函數,Person.prototype的隱式原型的constructor指向Object(),即Person.prototype.__proto__.constructor==Object() 從p.__proto__.constructor返回的結果為構造函數本身得到Person.prototype.constructor==Person()所以p.___proto__.__proto__==Object.prototype 所以p.b打印結果為b,p沒有b屬性,會一直通過__proto__向上查找,最后當查找到Object.prototype時找到,最后打印出b,向上查找過程中,得到的是Object.prototype,而不是Function.prototype,找不到a屬性,所以結果為undefined,這就是原型鏈,通過__proto__向上進行查找,最終到null結束 console.log(p.__proto__.__proto__.__proto__); //null console.log(Object.prototype.__proto__); //null 大家理解剛才的過程,相信下面這些應該也都明白 //Function function Function(){} console.log(Function); //Function() console.log(Function.prototype.constructor); //Function() console.log(Function.prototype.__proto__); //Object.prototype console.log(Function.prototype.__proto__.__proto__); //NULL console.log(Function.prototype.__proto__.constructor); //Object() console.log(Function.prototype.__proto__===Object.prototype); //true 總結: 1.查找屬性,如果本身沒有,則會去__proto__中查找,也就是構造函數的顯式原型中查找,如果構造函數中也沒有該屬性,因為構造函數也是對象,也有__proto__,那么會去它的顯式原型中查找,一直到null,如果沒有則返回undefined 2.p.__proto__.constructor==function Person(){} 3.p.___proto__.__proto__==Object.prototype 4.p.___proto__.__proto__.__proto__==Object.prototype.__proto__==null 5.通過__proto__形成原型鏈而非protrotype 最后附上一張圖,大家閱讀完之后,看圖應該可以很容易理解
###三、ES6、ES7、ES8 的相關知識點
ES6 塊級作用域 關鍵字let, 常量const 對象屬性賦值簡寫(property value shorthand) 解構賦值 函數參數 - 默認值、參數打包、 數組展開(Default 、Rest 、Spread) 箭頭函數 (Arrow functions) 字符串模板 Template strings ${} 迭代器(Iterators)for (var n of ['a','b','c']) 生成器 (Generators) class、constructor、extends、super Modules(模塊) 具有CommonJS的精簡語法、唯一導出出口(single exports)和循環依賴(cyclic dependencies)的特點。 類似AMD,支持異步加載和可配置的模塊加載。 Map + Set + WeakMap + WeakSet WeakMap、WeakSet作為屬性鍵的對象如果沒有別的變量在引用它們,則會被回收釋放掉。 Math + Number + String + Array + Object APIs Proxies:使用代理(Proxy)監聽對象的操作 .Symbols:調用symbol函數產生,它接收一個可選的名字參數,該函數返回的symbol是唯一的。 ?。?!Promises:處理異步操作的對象,用鏈式調用組織代碼 var promise=new Promise((resolve, reject)=> { this.login(resolve) }) //鏈式調用 .then(()=> this.getInfo()) .catch(()=> { console.log("Error") }) 4.ES7 求冪運算符(**) 3 ** 2 // 9 等價于: Math.pow(3, 2) // 9 Array.prototype.includes()方法:不能比較復雜類型數據,查找一個值在不在數組里,若在,則返回true,反之返回false。 ['a', 'b', 'c'].includes('a') // true 等價于 ['a', 'b', 'c'].indexOf('a') > -1 //true 函數作用域中嚴格(strict)模式的變更。 裝飾器(Decorator):修改類的行為 參數:target(所要修飾的目標類), name(所要修飾的屬性名), descriptor(該屬性的描述對象) 使用:npm install core-decorators –save // 將某個屬性或方法標記為不可寫。 @readonly // 標記一個屬性或方法,以便它不能被刪除; 也阻止了它通過Object.defineProperty被重新配置 @nonconfigurable // 立即將提供的函數和參數應用于該方法,允許您使用lodash提供的任意助手來包裝方法。 第一個參數是要應用的函數,所有其他參數將傳遞給該裝飾函數。 @decorate // 如果你沒有像Babel 6那樣的裝飾器語言支持,或者甚至沒有編譯器的vanilla ES5代碼,那么可以使用applyDecorators()助手。 @extendDescriptor // 將屬性標記為不可枚舉。 @nonenumerable // 防止屬性初始值設定項運行,直到實際查找修飾的屬性。 @lazyInitialize // 強制調用此函數始終將此引用到類實例,即使該函數被傳遞或將失去其上下文。 @autobind // 使用棄用消息調用console.warn()。 提供自定義消息以覆蓋默認消息。 @deprecate // 在調用裝飾函數時禁止任何JavaScript console.warn()調用。 @suppressWarnings // 將屬性標記為可枚舉。 @enumerable // 檢查標記的方法是否確實覆蓋了原型鏈上相同簽名的函數。 @override // 使用console.time和console.timeEnd為函數計時提供唯一標簽,其默認前綴為ClassName.method。 @time // 使用console.profile和console.profileEnd提供函數分析,并使用默認前綴為ClassName.method的唯一標簽。 @profile @noConcurrent 避免并發調用,在上一次操作結果返回之前,不響應重復操作 @makeMutex 多函數互斥,具有相同互斥標識的函數不會并發執行 @withErrToast 捕獲async函數中的異常,并進行錯誤提示 @mixinList 用于分頁加載,上拉加載時返回拼接數據及是否還有數據提示 @typeCheck 檢測函數參數類型 import {noConcurrent} from './decorators'; methods: { @noConcurrent //避免并發,點擊提交后,在接口返回之前無視后續點擊 async onSubmit(){ let submitRes=await this.$http({...}); //... return; } } methods: { @mixinList({needToast: false}) async loadGoods(params={}){ let goodsRes=await this.$http(params); return goodsRes.respData.infos; }, async hasMore() { let result=await this.loadgoods(params); if(result.state==='nomore') this.tipText='沒有更多了'; this.goods=result.list; } } // 上拉加載調用hasMore函數,goods數組就會得到所有拼接數據 // loadGoods可傳三個參數 params函數需要參數 ,startNum開始的頁碼,clearlist清空數組 // mixinList可傳一個參數 needToast 沒有數據是否需要toast提示 5.ES8 異步函數(Async function)使用形式: 函數聲明: async function foo() {} 函數表達式: const foo=async function() {} 對象的方式: let obj={ async foo() {} } 箭頭函數: const foo=async ()=> {} this.$http.jsonp('/login', (res)=> { this.$http.jsonp('/getInfo', (info)=> { // do something }) }) 異步編程機制:Generator async/await function * 函數名(){ yieId 'hello'; yieId 'world'; return 'ending'; } var hs=函數名(); hs.next(); // { value: 'hello', done: false } hs.next(); // { value: 'world', done: false } hs.next(); // { value: 'ending', done: true } hs.next(); // { value: undefined, done: true } //自動執行Generator函數 async function asyncFunc(params) { const result1=await this.login() const result2=await this.getInfo() } 調用 Generator 函數后,該函數并不執行,返回的也不是函數運行結果,而是一個指向內部狀態的指針對象,必須調用遍歷器對象的next方法,使得指針移向下一個狀態。每次調用next方法,內部指針就從函數頭部或上一次停下來的地方開始執行,直到遇到下一個yield表達式(或return語句)為止。 yieId:(產出),分段執行,yield表達式是暫停執行的標記,而next方法可以恢復執行 Object.entries()和Object.values() (1)Object.entries():具有鍵值對的數據結構,則每一個鍵值對都將會編譯成一個具有兩個元素的數組,這些數組最終會放到一個數組中,返回一個二維數組,若目標對象是數組時,則會將數組的下標作為鍵值返回。鍵值對中,如果鍵的值是Symbol,編譯時將會被忽略 Object.entries({ one: 1, two: 2 }) //[['one', 1], ['two', 2]] Object.entries([1, 2]) //[['0', 1], ['1', 2]] 如果對象的key值是數字,則返回值會對key值進行排序,返回的是排序后的結果。 Object.entries({ 3: 'a', 4: 'b', 1: 'c' }) //[['1', 'c'], ['3', 'a'], ['4', 'b']] //對象屬性的遍歷 let obj={ one: 1, two: 2 }; for (let [k,v] of Object.entries(obj)) { console.log(`${JSON.stringify(k)}: ${JSON.stringify(v)}`); } //輸出結果如下: 'one': 1 'two': 2 (2)Object.values():只返回自己的鍵值對中屬性的值 Object.values({ one: 1, two: 2 }) //[1, 2] Object.values({ 3: 'a', 4: 'b', 1: 'c' }) //['c', 'a', 'b'] 字符串填充:padStart和padEnd padStart函數:通過填充字符串的首部來保證字符串達到固定的長度,默認情況下使用空格填充 padEnd:填充字符串的尾部來保證字符串的長度的。 'Vue'.padStart(10) //' Vue' 'Vue'.padStart(10, '_*') //'_*_*_*_Vue' 'Vue'.padEnd(10, '_*') //'Vue_*_*_*_' Object.getOwnPropertyDescriptors():返回目標對象中所有屬性的屬性描述符,該屬性必須是對象自己定義的,不能是從原型鏈繼承來的。 該方法返回的描述符,會有兩種類型:數據描述符、存取器描述符 返回結果中包含的鍵可能的值有:configurable、enumerable、value、writable、get、set。 let obj={ id: 1, name: 'test', get gender() { console.log('gender') }, set grade(g) { console.log(g) } } Object.getOwnPropertyDescriptors(obj, 'id') //輸出結果為: { id: { configurable: true, enumerable: true, value: 1, writable: true } } 和assign區別 Object.assign(obj) //輸出結果為: { gender: undefined id: 1, name: 'test' } 共享內存和原子(共享陣列緩沖區,Shared memory and atomics)筆記待完善 新的構造函數SharedArrayBuffer、具有輔助函數的命名空間對象Atomics 多線程并發讀寫數據 添加尾部逗號而不報錯
###四、前端性能優化
1、雪碧圖技術 這個很簡單,把每個小圖標都整合到一張大圖上面,極大的減輕http請求數,同時能夠讓圖片快速加載進來。 考慮到當前的5g的發展前景,以后圖片不會造成加載延遲的現象。 2、瀏覽器渲染機制 輸入一個網址:我們得到服務端html文件。 根據html文件,從頭到尾的一個個的依次渲染頁面渲染頁面。 但是遇到圖片——不會等待圖片的加載完畢,會直接渲染下面的標簽。 如果圖片加載出來——根據圖片選擇,由于圖片要占用空間,決定是否重新加載頁面,這個概念叫reflow。(優化的方式——給圖片寬高)。 reflow和什么相關:占位面積、定位方式、邊距。 對于樣式中的顏色變化,叫做repaint、這個就只需要把顏色改變。所以性能上來說,repaint稍微比reflow高點。 repaint和什么相關:和顏色變化相關 3、webpack、gulp等打包工具的使用 壓縮代碼,減少了代碼體積。 可以把多個css文件,多個js文件,合并為一個css文件/js文件。 合并文件,讓我們減少了http請求數。 4、避免頁面跳轉,也就是使用單頁面應用的開發。 每次頁面跳轉,就是一次html文件的下載過程。而這個過程,我們首先從服務端下載網頁,再進行渲染,網頁性能體驗會很差。而單頁面應用,它從一開始,就把完整的網頁給加載到本地。 5、延遲加載、懶加載技術 什么是懶加載技術: 原理:先將img標簽中的src鏈接設為同一張圖片(空白圖片),將其真正的圖片地址存儲再img標簽的自定義屬性中(比如data-src)。當js監聽到該圖片元素進入可視窗口時,即將自定義屬性中的地址存儲到src屬性中,達到懶加載的效果。 這樣做能防止頁面一次性向服務器響應大量請求導致服務器響應慢,頁面卡頓或崩潰等問題。 6、將css放在HEAD中 如果將 CSS放在其他地方比如 BODY中,則瀏覽器有可能還未下載和解析到 CSS就已經開始渲染頁面了,這就導致頁面由無 CSS狀態跳轉到 CSS狀態,用戶體驗比較糟糕。除此之外,有些瀏覽器會在 CSS下載完成后才開始渲染頁面,如果 CSS放在靠下的位置則會導致瀏覽器將渲染時間推遲。 7、Vue項目的按需加載 vue中的懶加載是通過webpack的代碼分割來實現的,下面是官網文檔:https://router.vuejs.org/zh-cn/advanced/lazy-loading.html 主要是在加載路由的時候,使用: const Main=r=> require.ensure([], ()=> r(require(‘../views/main/Main’))) require.ensure就是webpack提供的異步加載的方式。 8、關于React的性能優化 react中,進行性能優化的核心就是shouldComponentDidMount周期函數。 9、設置合理的http緩存 http請求中,我們可以合理的設置headers,就能達到緩存的目的。 有兩種常見的緩存,強制緩存和對比緩存: 第一種:強制緩存。 直接訪問瀏覽器中的緩存數據,如果存在,則直接使用瀏覽器中的數據。如果不存在,則再向服務器發送請求,然后得到服務器的數據,再把這些數據存在瀏覽器中。 第二種:對比緩存。 首先,獲取瀏覽器中的數據緩存標識,再獲取服務器上面的數據緩存標識,如果相匹配,則直接從瀏覽器中獲取數據,如果不匹配,則從服務器上獲取數據。 關于緩存標識,有兩類標識: 第一類: 第一次請求,服務器會返回一個Last-Modified。 下一次請求,瀏覽器會自動在headers中添加一條If-Modified-Since屬性,記錄的就是上一次數據發生修改的時間。 第二類: 第一次請求,服務端返回一個Etag資源唯一標識符。 第二次請求,瀏覽器會自動攜帶一個If-None_Match標識符。 應用程序緩存 創建cache manifest文件,通過給html文件中的HTML標簽添加一個manifest屬性來實現的。 <!DOCTYPE HTML> <html manifest="demo.appcache"> <body> 文檔內容...... </body> </html>
###五、Vue、React、Angular 的異同點
1、Angular特性: 由自己實現一套模板編譯規則,數據變化依賴臟檢查, 基本屬性包括:數據雙向綁定、基本模板指令、自定義指令、表單驗證、路由操作、依賴注入、過濾器、內置服務、自定義服務、組件、模塊。 運行效率較低,數據變更檢測方式。 學習angular會迫使你學習特有的預發,上手成本很大,代碼看起來很干凈 依賴注入,即一個對象將依賴項提供給另一個對象(客戶端)的模式。導致更多的靈活性和更干凈的代碼。 Angular 最適合單頁應用(SPA),因為它可能太臃腫而不能用于微服務。 框架比較臃腫,每次用啥功能要引入一大堆東西 Angular錯誤提示不夠清晰明顯,對于初級開發者,很難看懂Angular的錯誤提示。(個人認為這是最大的不好之處,當初學習這個遇到很多坑?。叶ㄎ籦ug很難。 面向對象編程的思想,Angular由后端開發人員設計的前端框架。 詳細比較:React和Vue的區別 2、React特性: 單向綁定,先更新model,然后渲染UI元素,數據在一個方向流動,使得調試更加容易。代碼冗余,各種生命周期太麻煩,剛開始接觸好難記。 用了虛擬DOM。(對虛擬DOM的理解剛開始我不是很理解概念,建議大家去看【深入REACT技術?!窟@本書有很好的講解) 更適合大型應用和更好的可測試性 Web端和移動端原生APP通吃 更大的生態系統,更多的支持和好用的工具 3、Vue特性 模板和渲染函數的彈性選擇 簡單的語法和項目配置 更快的渲染速度和更小的體積四 4、Vue和React共同點 用虛擬DOM實現快速渲染 輕量級 響應式組件 服務端渲染 集成路由工具,打包工具,狀態管理工具的難度低 5:不同點 vue 控制器:無;過濾器 :無 ;指令:有;渲染指令: 有 ;數據綁定:雙向; React 控制器:無;過濾器 :無 ;指令:無;渲染指令 : 無 ;數據綁定:單向; angular 控制器:有;過濾器 :有 ;指令:有;渲染指令 : 有 ;數據綁定:雙向;
###六、Webpack 如何對項目構建優化
請移駕我的另一篇 Chat 文章https://gitbook.cn/gitchat/activity/5bed34555748cb6bd2780d4b
###七、首屏優化
1. DNS預解析 2.域名收斂 既然DNS解析比較耗時,每個連接都要建立鏈路,那么我們就可以通過減少ajax到后臺的域名地址,通過反向代理去做。 3. 鏈路復用 因為每一次鏈接數都要建立3次TCP握手,通過keep-alive,可以保證建立的連接不會被關閉, 下次請求可以直接發送數據,這樣可以減少將近200ms的時間,當然會增加一部分Server的內存消耗,要預先擴容;http2.0已經支持了這些特性; 4. 資源內聯 在首屏里,一些資源都是分散開發的,在一些簡單的頁面中,有些內容可以內聯進去,不然像一些css頁面要等到html頁面解析完成后,再去解析外聯的css; 可以通過打包工具在發布的時候就能完成; 5.組件化開發 首先一點是按需加載,就是首屏顯示的東西,哪些要加載,哪些無需加載,可以區分開來,而且可以異步方式來加載。 我們通常是這么做的,我們可以先加載一些公用的東西,然后通過路由,去加載首屏上可以看到選項卡的資源,哪些有用戶交互之后的東西,可以稍后在加載; 但是這樣會有一個問題,有時候我們的js處理的比較快,但是css文件處理的比較快,這樣就會使的頁面比較混亂,所以可以通過異步打包工具,將css和js打包在一起 6. 服務端渲染 一個前后端分離的項目,通過ajax請求數據,這樣一個頁面的渲染路徑會拉的很長,頁面先渲染html,然后在渲染js,js才發送ajax請求,等到數據回來再進行渲染,所以不管怎么樣都至少要3個RTT請求時間;這種模式,在網絡環境比較差的情況下,是很難接受的, 所以我們又回歸到PHP時代;通過服務器端渲染,就可以控制用戶,哪些東西是要打包的??梢砸淮涡灾烙脩粜枰男祿?;加上現在的NodeJs環境以及React,這就使得一些東西既可以在前端的瀏覽器進行渲染,也可以在服務器中進行字符串拼接,一起吐出來; 比如在無限長的滾動列表中,對圖片進行懶加載,首先不給圖片賦值src屬性,通過插入dom計算可視區,在可視區內則賦值src,在可視區之外,交給scroll去處理,將要進入可視區時才賦值; 7.利用緩存 資源長緩存:不是通過304去判斷緩存,而是通過max-age來做的,通過md5命名文件,然后通過自動化構建工具去實現;主要聊一下數據層緩存與數據復用; 從客戶端的啟發;客戶端會將上次緩存的數據重新渲染一遍;所以數據緩存的設計可以設計為 可以在ajax請求的數據沒有回來之前,用一些配置的假數據,先把坑站好,然后等待線上的數據回來后,在把坑填好,這樣減少了白屏的時間。這樣給用戶感覺比較快,但不是真的快,交互性能確實變好了; 在有些時候,并不是所有的數據都是加載進來的,像貼吧和知乎之類的,展示的一些storage里的數據,都是帶有省略號的緩存數據,等用戶真的點開之后才去加載該帖子的全部數據。每次請求的數據又重新將原來的緩存覆蓋; 8.離線包 其實對于一些并發量比較大,向過年時候的搶紅包業務,并發量是幾個億。這樣,對于一些經常使用的用戶,可以幾天前就將離線包推送過去,進行下載,通過二進制比較,進行動態增量更新,而不用每次都下載,這么大的并發量,同時下載,帶寬肯定爆,服務器肯定爆。 9.減少請求次數 10.減少對dom的操作
###七、移動端如何做適配
現在最常用的就是一稿設計多端適配方案—— rem + flexible rem是什么? rem(font size of the root element)是指相對于根元素的字體大小的單位。簡單的說它就是一個相對單位??吹絩em大家一定會想起em單位,em(font size of the element)是指相對于父元素的字體大小的單位。它們之間其實很相似,只不過一個計算的規則是依賴根元素一個是依賴父元素計算。 REM自適應JS //designWidth:設計稿的實際寬度值,需要根據實際設置 //maxWidth:制作稿的最大寬度值,需要根據實際設置 //這段js的最后面有兩個參數記得要設置,一個為設計稿實際寬度,一個為制作稿最大寬度,例如設計稿為750,最大寬度為750,則為(750,750) ;(function(designWidth, maxWidth) { var doc=document, win=window, docEl=doc.documentElement, remStyle=document.createElement("style"), tid; function refreshRem() { var width=docEl.getBoundingClientRect().width; maxWidth=maxWidth || 540; width>maxWidth && (width=maxWidth); var rem=width * 100 / designWidth; remStyle.innerHTML='html{font-size:' + rem + 'px;}'; } if (docEl.firstElementChild) { docEl.firstElementChild.appendChild(remStyle); } else { var wrap=doc.createElement("div"); wrap.appendChild(remStyle); doc.write(wrap.innerHTML); wrap=null; } //要等 wiewport 設置好后才能執行 refreshRem,不然 refreshRem 會執行2次; refreshRem(); win.addEventListener("resize", function() { clearTimeout(tid); //防止執行兩次 tid=setTimeout(refreshRem, 300); }, false); win.addEventListener("pageshow", function(e) { if (e.persisted) { // 瀏覽器后退的時候重新計算 clearTimeout(tid); tid=setTimeout(refreshRem, 300); } }, false); if (doc.readyState==="complete") { doc.body.style.fontSize="16px"; } else { doc.addEventListener("DOMContentLoaded", function(e) { doc.body.style.fontSize="16px"; }, false); } })(750, 750); 第一個參數是設計稿的寬度,一般設計稿有640,或者是750,你可以根據實際調整 第二個參數則是設置制作稿的最大寬度,超過750,則以750為最大限制。 在小王待過的公司里面,設計稿都是按750來設計的。不過管它用什么尺寸來設計,適配理念都一樣,以不變應萬變。 使用1rem=100px轉換你的設計稿的像素,例如設計稿上某個塊是100px*300px,換算成rem則為1rem*3rem。 這樣子不管什么類型的手機,就都可以是適配了。像那種用多媒體查詢針對不同設備尺寸寫樣式的方式,就讓見鬼去吧。
###八、跨終端如何做適配
先來個圖壓壓驚,圖有點老,現在都iPhonx了。
如果要適配這么多設備是不是很頭疼!??!
###九、MVVM 框架的知識點。
####Vue 部分
Vue組件之間如何通信 一. 父子之間的通信 1. 父組件-》子組件(props down) ①通過屬性 步驟1:父組件在調用子組件時傳值 <son myName="michael" myPhone='123'></son> <son :myName="userList[0]"></son> 步驟2:子組件通過props得到父組件的傳過來的數據 Vue.component('son',{ props:['myName','myPhone'] }) ②通過$parent 直接在子組件中通過this.$parent得到調用子組件的父組件 2、子組件-》父組件(events up) ①events up 步驟1:在父組件中 調用子組件的時候 綁定一個自定義事件 和 對應的處理函數 methods:{ recvMsg:function(msg){ //msg就是傳遞來的數據 } }, template:' <son @customEvent="recvMsg"></son> ' 步驟2:在子組件中 把要發送的數據通過觸發自定義事件傳遞給父組件 this.$emit('customEvent',123) ② $refs 步驟1:在調用子組件的時候 可以指定ref屬性 `<son ref='zhangsan'></son>` 步驟2:通過$refs 得到指定引用名稱對應的組件實例 this.$refs.zhangsan 二、兄弟組件間的通信 步驟1:創建一個Vue的實例 作為事件綁定觸發的公共的對象 var bus=new Vue(); 步驟2:在接收方的組件 綁定 自定義的事件 bus.$on('customEvent',function(msg){ console.log(msg); //msg是通過事件傳遞來的數據 (傳遞來的123) }); 步驟3:在發送方的組件 觸發 自定義的事件 bus.$emit('customEvent',123); 除了以上幾種方式外,還有 Vuex、路由傳參和緩存。 二、Vue的雙向數據綁定原理是什么? vue.js 是采用數據劫持結合發布者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在數據變動時發布消息給訂閱者,觸發相應的監聽回調。 第一步:需要observe的數據對象進行遞歸遍歷,包括子屬性對象的屬性,都加上 setter和getter 這樣的話,給這個對象的某個值賦值,就會觸發setter,那么就能監聽到了數據變化 第二步:compile解析模板指令,將模板中的變量替換成數據,然后初始化渲染頁面視圖,并將每個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變動,收到通知,更新視圖 第三步:Watcher訂閱者是Observer和Compile之間通信的橋梁,主要做的事情是: 1、在自身實例化時往屬性訂閱器(dep)里面添加自己 2、自身必須有一個update()方法 3、待屬性變動dep.notice()通知時,能調用自身的update()方法,并觸發Compile中綁定的回調,則功成身退。 第四步:MVVM作為數據綁定的入口,整合Observer、Compile和Watcher三者,通過Observer來監聽自己的model數據變化,通過Compile來解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通信橋梁,達到數據變化 -> 視圖更新;視圖交互變化(input) -> 數據model變更的雙向綁定效果。 三、詳細說下你對vue生命周期的理解? 總共分為8個階段創建前/后,載入前/后,更新前/后,銷毀前/后 創建前/后: 在beforeCreated階段,vue實例的掛載元素$el和數據對象data都為undefined,還未初始化。在created階段,vue實例的數據對象data有了,$el還沒有。 載入前/后:在beforeMount階段,vue實例的$el和data都初始化了,但還是掛載之前為虛擬的dom節點,data.message還未替換。在mounted階段,vue實例掛載完成,data.message成功渲染。 更新前/后:當data變化時,會觸發beforeUpdate和updated方法。 銷毀前/后:在執行destroy方法后,對data的改變不會再觸發周期函數,說明此時vue實例已經解除了事件監聽以及和dom的綁定,但是dom結構依然存在 四、你是怎么理解vuex的? vuex可以理解為一種開發模式或框架。 通過狀態(數據源)集中管理驅動組件的變化(好比spring的IOC容器對bean進行集中管理)。 應用級的狀態集中放在store中; 改變狀態的方式是提交mutations,這是個同步的事物; 異步邏輯應該封裝在action中。 五、聊聊你對Vue.js的template編譯的理解? 簡而言之,就是先轉化成AST樹,再得到的render函數返回VNode(Vue的虛擬DOM節點) 首先,通過compile編譯器把template編譯成AST語法樹(abstract syntax tree 即 源代碼的抽象語法結構的樹狀表現形式),compile是createCompiler的返回值,createCompiler是用以創建編譯器的。另外compile還負責合并option。 然后,AST會經過generate(將AST語法樹轉化成render funtion字符串的過程)得到render函數,render的返回值是VNode,VNode是Vue的虛擬DOM節點,里面有(標簽名、子節點、文本等等)
####React 部分
一、react生命周期及相關用法 react生命周期分為初始化階段、運行階段、銷毀階段。 (1) 初始化階段: getDefaultProps:獲取實例的默認屬性 getInitialState:獲取每個實例的初始化狀態 componentWillMount:實例掛載之前 Render:渲染組件 componentDidMount:實例掛載完成。一般在這個函數中與后臺進行初始化數據交互。 (2)運行階段: componentWillReceiveProps:父組件改變時調用。 sholudComponentUpdate:主要是用來手動阻止組件渲染,一般在這個函數中做組件的性能優化。 componentWillUpdate:組件數據更新前調用 componentDidUpdate:組件數據更新完成時調用 (3)銷毀階段: componentUnmount:銷毀階段。一般用來銷毀不用的變量或者是解除無用定時器以及解綁無用事件。防止內存泄漏問題。 二、你了解 Virtual DOM 嗎?解釋一下它的工作原理。 Virtual DOM 是一個輕量級的 JavaScript 對象,它最初只是 real DOM 的副本。它是一個節點樹,它將元素、它們的屬性和內容作為對象及其屬性。 React 的渲染函數從 React 組件中創建一個節點樹。然后它響應數據模型中的變化來更新該樹,該變化是由用戶或系統完成的各種動作引起的。 Virtual DOM 工作過程有三個簡單的步驟。 1.每當底層數據發生改變時,整個 UI 都將在 Virtual DOM 描述中重新渲染。 2.然后計算之前 DOM 表示與新表示的之間的差異。 3.完成計算后,將只用實際更改的內容更新 real DOM。 三、總結一下Redux的三大原則和數據流的管理 Redux三大原則: 1、單一數據源,這個應用的state被存儲在一棵object tree中,并且這個object tree只存在于唯一的Store中。 2、state是只讀的,唯一改變state的方法就是觸發action,action是一個用于描述已發生事件的普通對象。 3、使用純函數來執行修改,為了描述action如何改變state tree,需要編寫reducer。 4、具體工作步驟如下:
Redux數據流的管理:
1、action:把數據傳遞到Store,唯一數據來源。
2、reducer:action只描述有事情發生,reducer指明如何更新state,即設計state結構和action處理。
3、Store:把action和reducer聯系到一起,負責維持、獲取和更新state。
4、生命周期:數據流嚴格且單向
調用Store.dispatch(action)->Store調用傳入的reducer函數,Store會把兩個參數傳入reducer:當前的state樹和action->根reducer將多個子reducer輸出合并成一個單一的state樹->Store保存了根reducer,并返回完整的state樹。
四、Redux與它的中間件
redux是一個可預測的狀態容器,
react-redux是將store和react結合起來,使得數據展示和修改對于react項目而言更簡單
redux中間件就是在dispatch action前對action做一些處理
redux-thunk用于對異步做操作
redux-actions用于簡化redux操作
redux-promise可以配合redux-actions用來處理Promise對象,使得異步操作更簡單
redux-sage可以起到一個控制器的作用,集中處理邊際效用,并使得異步操作的寫法更優雅。
//這里還需要大家自己再去多了解一下redux中間件的知識。
###十、實現一個觀察者模式
舉個生活比較常見常見的例子,比如你去面試之后,面試官看你表現不錯,最后會跟你要聯系方式,以便之后可以聯系你。在這角色扮演當中,你就是“訂閱者”,面試官就是“發布者”。 那么發布訂閱模式是咋實現的呢? 思路: 給定一個發布者 面試者將聯系方式給發布者 發布者的一個列表有各種職位(web端的,java 的),里面記載回調函數以便通知這些面試者 最后發布消息的時候,會遍歷這個列表的職位的回調函數,告訴面試者面試這個職位是通過還是不通過 如果面試者取消了訂閱,那么將回調函數和之前的回調函數作對比,如果相等,就將這個面試者的上班通知去掉 var Event=(function() { var events={}; //發布者 //subscribe也就是訂閱,post 代表面試者要面的職位,callback表示為回調函數 function subscribe(post, callback) { events[post]=events[post] || []; //發布者的列表里有沒有這個面試職位,如果沒有就創建一個空數組 events[post].push(callback); } //publish 表示發布 function publish() { var post=Array.prototype.shift.call(arguments); //第一個參數指定“鍵” var fns=events[post]; //設置緩存,提高性能 if (!fns) { //如果發布者的列表里沒有這個職位,那肯定是不能發布 return; } for (var i=0; i < fns.length; i++) { //遍歷當前的職位的數組里有幾個面試者 fns[i].apply(this, arguments); } } //unsubscribe 表示取消訂閱 function unsubscribe(post, fn) { var fns=events[post]; if (fns) { if (fn) { for (var i=fns.length; i >=0; i--) { if (fns[i]===fn) fns.splice(i, 1); } } else {//如果沒有傳入fn回調函數,直接取消post對應消息的所有訂閱 fns=[]; } } else {//如果發布者的列表沒有這個職位,直接 return return; } } return { subscribe: subscribe, publish: publish, unsubscribe: unsubscribe }; })(); 測試: var fn1=function(time) { console.log("小明你通過了面試,上班時間:" + time); }; var fn2=function(time) { console.log("小強你通過了面試,上班時間:" + time); }; //小明將聯系方式給了發布者,發布者(hr)覺得小明不錯,可以通過,于是在列表(java)里寫下了一些回調函數,到時候發布的時候將上班時間告訴小明 Event.subscribe("java", fn1); //小強也訂閱了 Event.subscribe("java", fn2); Event.publish("java", "2017-10-01"); /*輸出: 小明你通過了面試,上班時間:2017-10-01 小強你通過了面試,上班時間:2017-10-01 */ Event.unsubscribe("java", fn1);//刪除小明的上班通知 Event.publish("java", "2017-10-01"); /*輸出: 小強你通過了面試,上班時間:2017-10-01 */
###十一、實現一個數組去重的函數
//其實數組去重的方法有好多種,這就介紹常用的幾種就夠了 第一種: let arr=[1, 'a', 'a', 'b', 'd', 'e', 'e', 1, 0, 2, 2, 3]; function unique(arr){ return [...(new Set(arr))]; } console.log(unique(arr)); // [1, "a", "b", "d", "e", 0, 2, 3] 第二種: let arr=[1, 'a', 'a', 'b', 'd', 'e', 'e', 1, 0, 2, 2, 3]; function unique(arr){ return Array.from(new Set(arr)); } console.log(unique(arr)); // [1, "a", "b", "d", "e", 0, 2, 3] 第三種: let arr=[1, 'a', 'a', 'b', 'd', 'e', 'e', 1, 0, 2, 2, 3]; function unique(arr){ return arr.reduce((prev,cur)=> prev.includes(cur) ? prev : [...prev,cur],[]); } console.log(unique(arr)); // [1, "a", "b", "d", "e", 0, 2, 3] 第四種:最普通的一種,想了解更多,自己多多研究吧。 let arr=[1, 'a', 'a', 'b', 'd', 'e', 'e', '1', 0, 2, 2, 3]; function unique(arr){ let newArr=[]; let obj={}; for (let i=0; i < arr.length; i++) { if (!obj[typeof arr[i] + arr[i]]) { obj[typeof arr[i] + arr[i]]=1; newArr.push(arr[i]); } } return newArr; } console.log(unique(arr)); // [1, "a", "b", "d", "e", "1", 0, 2, 3]
###十二、介紹Promise的原理
在Promise的內部,有一個狀態管理器的存在,有三種狀態:pending、fulfilled、rejected。 (1) promise 對象初始化狀態為 pending。 (2) 當調用resolve(成功),會由pending=> fulfilled。 (3) 當調用reject(失敗),會由pending=> rejected。 因此,看上面的的代碼中的resolve(num)其實是將promise的狀態由pending改為fulfilled,然后向then的成功回掉函數傳值,reject反之。但是需要記住的是注意promsie狀態 只能由 pending=> fulfilled/rejected, 一旦修改就不能再變(記住,一定要記住,下面會考到)。 當狀態為fulfilled(rejected反之)時,then的成功回調函數會被調用,并接受上面傳來的num,進而進行操作。promise.then方法每次調用,都返回一個新的promise對象 所以可以鏈式寫法(無論resolve還是reject都是這樣)。 Promise也是面試必問的一個知識點,多多學習?! ?
###十三、JavaScript對象的淺拷貝與深拷貝實例分析
1、淺拷貝
僅僅復制對象的引用,而不是對象本身
var person={
name: 'Alice',
friends: ['Bruce', 'Cindy']
}
var student={
id: 30
}
student=simpleClone(person, student);
student.friends.push('David');
function simpleClone(oldObj, newObj) {
var newObj=newObj || {};
for (var i in oldObj)
newObj[i]=oldObj[i];
return newObj;
}
console.log(person.friends);//["Bruce", "Cindy", "David"]
2、深拷貝
把復制的對象所引用的全部對象都復制一遍,能夠實現真正意義上的數組和對象的拷貝。
淺拷貝的問題:如果父對象的屬性值為一個數組或另一個對象,那么實際上子對象獲得的只是一個內存地址,而不是對父對象的真正拷貝,因此存在父對象被篡改的可能。
方法1:
var person={
name: 'Alice',
friends: ['Bruce', 'Cindy']
}
var student={
id: 30
}
student=deepClone(person, student);
student.friends.push('David');
function deepClone(oldObj, newObj) {
var newObj=newObj || {};
newObj=JSON.parse(JSON.stringify(oldObj));
return newObj;
}
console.log(person.friends); // 'Bruce', 'Cindy'
方法2:
function deepClone(oldObj, newObj) {
var newObj=newObj || {};
for (var i in oldObj) {
var prop=oldObj[i];
if (prop===newObj)
continue;
if (typeof prop==='object')
newObj[i]=(prop.constructor===Array) ? [] : Object.create(prop);
else
newObj[i]=prop;
}
return newObj;
}
###十四、徹底弄清 Callback、Promise、Generator以及Async/await
一、回調函數 所謂回調函數(callback),就是把任務分成兩步完成,第二步單獨寫在一個函數里面,等到重新執行這個任務時,就直接調用這個函數。 例如Node.js中讀取文件 fs.readFile('a,txt', (err,data)=>{ if(err) throw err; console.log(data); }) 上面代碼中readFile的第二個參數就是回調函數,等到讀取完a.txt文件時,這個函數才會執行。 二、Promise 使用回調函數本身沒有問題,但有“回調地獄”的問題。 假定我們有一個需求,讀取完A文件之后讀取B文件,再讀取C文件,代碼如下 fs.readFile(fileA, (err, data)=> { fs.readFile(fileB, (err, data)=> { fs.readFile(fileC, (err,data)=>{ //do something }) }); }); 可見,三個回調函數代碼看來就夠嗆了,有時在實際業務中還不止嵌套這幾個,難以管理。 這時候Promise出現了!它不是新的功能,而是一種新的寫法,用來解決“回調地獄”的問題。 我們再假定一個業務,分多個步驟完成,每個步驟都是異步的,而且依賴于上一個步驟的結果,用setTimeout()來模擬異步操作 /** * 傳入參數 n,表示這個函數執行的時間(毫秒) * 執行的結果是 n + 200,這個值將用于下一步驟 */ function A(n) { return new Promise(resolve=> { setTimeout(()=> resolve(n + 200), n); }); } function step1(n) { console.log(`step1 with ${n}`); return A(n); } function step2(n) { console.log(`step2 with ${n}`); return A(n); } function step3(n) { console.log(`step3 with ${n}`); return A(n); } 上面代碼中有4個函數,A()返回一個Promise對象,接收參數n,n秒后執行resolve(n+200)。step1、 step2、step3對應三個步驟 現在用Promise實現這三個步驟: function doIt() { console.time('do it now') const time1=300; step1(time1) .then( time2=>step2(time2)) .then( time3=> step3(time3)) .then( result=> { console.log(`result is ${result}`) }); } doIt(); 輸出結果如下 step1 with 300 step2 with 500 step3 with 700 result is 900 result是step3()的參數700+200=900。 可見,Promise的寫法只是回調函數的改進,用then()方法免去了嵌套,更為直觀。 但這樣寫絕不是最好的,代碼變得十分冗余,一堆的then。 所以,最優秀的解決方案是什么呢? 開頭暴露了,就是async/await 講async前我們先講講協程與Generator 三、協程 協程(coroutine),意思是多個線程相互協作,完成異步任務。 它的運行流程如下 協程A開始執行 協程A執行到一半,暫停執行,執行的權利轉交給協程B。 一段時間后B交還執行權 協程A重得執行權,繼續執行 上面的協程A就是一個異步任務,因為在執行過程中執行權被B搶了,被迫分成兩步完成。 讀取文件的協程代碼如下: function task() { // 其他代碼 var f=yield readFile('a.txt') // 其他代碼 } task()函數就是一個協程,函數內部有個新單詞yield,yield中文意思為退讓, 顧名思義,它表示執行到此處,task協程該交出它的執行權了。也可以把yield命令理解為異步兩個階段的分界線。 協程遇到yield命令就會暫停,把執行權交給其他協程,等到執行權返回繼續往后執行。最大的優點就是代碼寫法和同步操作幾乎沒有差別,只是多了yield命令。 這也是異步編程追求的,讓它更像同步編程 四、Generator函數 Generator是協程在ES6的實現,最大的特點就是可以交出函數的執行權,懂得退讓。 function* gen(x) { var y=yield x +2; return y; } var g=gen(1); console.log( g.next()) // { value: 3, done: false } console.log( g.next()) // { value: undefined, done: true } 上面代碼中,函數多了*號,用來表示這是一個Generator函數,和普通函數不一樣,不同之處在于執行它不會返回結果, 返回的是指針對象g,這個指針g有個next方法,調用它會執行異步任務的第一步。 對象中有兩個值,value和done,value 屬性是 yield 語句后面表達式的值,表示當前階段的值,done表示是否Generator函數是否執行完畢。 下面看看Generator函數如何執行一個真實的異步任務 var fetch=require('node-fetch'); function* gen(){ var url='https://api.github.com/users/github'; var result=yield fetch(url); console.log(result.bio); } var g=gen(); var result=g.next(); result.value.then( data=> return data.json) .then (data=> g.next(data)) 上面代碼中,首先執行Generator函數,得到對象g,調用next方法,此時 result={ value: Promise { <pending> }, done: false } 因為fetch返回的是一個Promise對象,(即value是一個Promise對象)所以要用then才能調用下一個next方法。 雖然Generator將異步操作表示得很簡潔,但是管理麻煩,何時執行第一階段,又何時執行第二階段? 是的,這時候到Async/await出現了! 五、Async/await 從回調函數,到Promise對象,再到Generator函數,JavaScript異步編程解決方案歷程可謂辛酸,終于到了Async/await。很多人認為它是異步操作的最終解決方案(謝天謝地,這下不用再學新的解決方案了吧) 其實async函數就是Generator函數的語法糖,例如下面兩個代碼: var gen=function* (){ var f1=yield readFile('./a.txt'); var f2=yield readFile('./b.txt'); console.log(f1.toString()); console.log(f2.toString()); }; var asyncReadFile=async function (){ var f1=await readFile('./a.txt'); var f2=await readFile('./b.txt'); console.log(f1.toString()); console.log(f2.toString()); }; 上面的為Generator函數讀取兩個文件,下面為async/await讀取,比較可發現,兩個函數其實是一樣的,async不過是把Generator函數的*號換成async,yield換成await。 1.async函數用法 上面說了async不過是Generator函數的語法糖,那為什么要取這個名字呢?自然是有理由的。 async是“異步”,而await是async wait的簡寫,即異步等待。所以應該很好理解async用于聲明一個function是異步的,await用于等待一個異步方法執行完成 下面來看一個例子理解async命令的作用 async function test() { return "async 有什么用?"; } const result=test(); console.log(result) 輸出: Promise { 'async 有什么用?' } 可以看到,輸出的是一個Promise對象! 所以,async函數返回的是一個Promise對象,如果直接return 一個直接量,async會把這個直接量通過PromIse.resolve()封裝成Promise對象 注意點 一般來說,都認為await是在等待一個async函數完成,確切的說等待的是一個表示式,這個表達式的計算結果是Promise對象或者是其他值(沒有限定是什么) 即await后面不僅可以接Promise,還可以接普通函數或者直接量。 同時,我們可以把async理解為一個運算符,用于組成表達式,表達式的結果取決于它等到的東西 等到非Promise對象 表達式結果為它等到的東西 等到Promise對象 await就會阻塞后面的代碼,等待Promise對象resolve,取得resolve的值,作為表達式的結果 還是那個業務,分多個步驟完成,每個步驟依賴于上一個步驟的結果,用setTimeout模擬異步操作。 /** * 傳入參數 n,表示這個函數執行的時間(毫秒) * 執行的結果是 n + 200,這個值將用于下一步驟 */ function takeLongTime(n) { return new Promise(resolve=> { setTimeout(()=> resolve(n + 200), n); }); } function step1(n) { console.log(`step1 with ${n}`); return takeLongTime(n); } function step2(n) { console.log(`step2 with ${n}`); return takeLongTime(n); } function step3(n) { console.log(`step3 with ${n}`); return takeLongTime(n); } async實現方法 async function doIt() { console.time("doIt"); const time1=300; const time2=await step1(time1); const time3=await step2(time2); const result=await step3(time3); console.log(`result is ${result}`); console.timeEnd("doIt"); } doIt(); 輸出結果和上面用Promise實現是一樣的,但這個代碼結構看起來清晰得多,幾乎跟同步寫法一樣。 2. async函數的優點 (1)內置執行器 Generator 函數的執行必須靠執行器,所以才有了 co 函數庫,而 async 函數自帶執行器。也就是說,async 函數的執行,與普通函數一模一樣,只要一行。 (2) 語義化更好 async 和 await,比起星號和 yield,語義更清楚了。async 是“異步”的簡寫,而 await 可以認為是 async wait 的簡寫。所以應該很好理解 async 用于申明一個 function 是異步的,而 await 用于等待一個異步方法執行完成。 (3)更廣的適用性 yield 命令后面只能是 Thunk 函數或 Promise 對象,而 async 函數的 await 命令后面,可以跟 Promise 對象和原始類型的值(數值、字符串和布爾值,但這時等同于同步操作)。
以上就是中大廠的面試知識點匯總,算不上包羅萬象,但是涵蓋的知識點還是比較齊全的,只要你能認真看完本文并理解了,那面試應該是沒啥大問題的。當然,其中也少不了你自己的努力,還有更多的知識點需要你去學習。前端就是這樣,招聘的是工程師,干活卻是螺絲釘。這就要求各位前端coder們,不能只注重業務,平時也要給自己充電,知其然知其所以然,擴展自己的知識范圍,懂得越多,在前端這條路上也才能走的更穩、更遠。
最后,希望看過我文章的都找到好工作,也不枉我辛苦一場?。?!
學習網頁的概念和分類,了解靜態網頁和動態網頁的不同;了解網頁瀏覽器的工作原理。了解HTML,XHTML,HTML5的概念,制作簡單的HTML頁面的開發。
可以在internet上通過網頁瀏覽信息,如新聞,圖片等,還可發布信息,如招聘信息等,網頁是在某個地方某一臺計算機上的一個文件。
網頁主要由3部分組成:結構,表現,行為。
靜態網頁的特點是不論在何時何地瀏覽這個網頁,看到的形式和內容都相同,且只能瀏覽,用戶無法與網站進行互動。靜態頁面由HTML編寫,擴展名一般為.htm, .html, .shtml, .xml等。與動態頁面相比,動態網頁是以.asp, .jsp, .php, .perl, .cgi等形式為后綴。
動態網頁指網頁的內容可以根據某種條件而自動改變。
采用B/S結構,即瀏覽器/服務器結構,用戶工作界面是通過www瀏覽器來實現的:
瀏覽器的工作原理:
www的基礎是HTTP協議,web瀏覽器就是用于通過url來獲取并顯示web網頁的一種軟件工具,url用于指定要取得的Internet上資源的位置與方式。
HTML是一種用來制作超文本文檔的簡單標記語言,用其編寫的超文本文檔稱為HTML文檔,它能獨立于各種操作系統平臺。
可擴展超文本標記語言XHTML:
XHTML是不需要編譯,可以直接由瀏覽器執行,是一種增強了的HTML。它的可擴展性和靈活性將適應未來網絡應用的更多需求,是基于XML的應用。開發者在HTML4.0的基礎上,用XML的規則對其進行一些擴展,由此得到了XHTML,所以,建立XHTML的目的是為了實現HTML向xml的過渡。
HTML5簡化了:<!DOCTYPE html>,簡化了DOCTYPE,簡化了字符集聲明,以瀏覽器的原生能力替代腳本代碼的實現,簡單而強大的HTML5API。
文件擴展名是操作系統用來標志文件格式的一種機制。擴展名如同文件的身份說明,區別了文件的類別和作用。
HTML網頁的文件后綴名是.html或者.htm.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"xxx">
聲明的作用,告訴瀏覽器所書寫的HTML代碼的版本。
復制代碼
<meta>標簽,是HTML文檔<head>標簽內的一個輔助性標簽,meta標簽分為2個重要的屬性:name和http-equiv,通常用于能夠優化頁面被搜索的可能性。
meta標簽下name屬性的使用:
<head>
<meta name="keywords" content="nine, twenty-three">
<meta name="description" content="...">
<meta name="generator" content="Dreamweaver">
<meta name="author" content="depp">
<meta name="robots" content="all">
</head>
復制代碼
meta標簽下的另一個屬性http-equiv,其作用是反饋給瀏覽器一些明確的信息,幫助瀏覽器更精確地展示頁面。
<head>
<meta http-equiv="content-type" content="text/html; charset=gb2312"/>
</head>
復制代碼
link標簽,定義了一個外部文件的鏈接,經常被用于鏈接外部css樣式。
base標簽為整個頁面定義了所有鏈接的基礎定位,其主要的作用是確保文檔中所有的相對url都可以被分解成確定的文檔地址。
style標簽用于定義css的樣式。表明了在頁面中引入一個.style的樣式表。
script標簽用于定義頁面內的腳本。
titl標題標簽,body體標簽.
一個好的HTML文檔應具備以下3個方面:
<br>..</br>
<p>...</p>
復制代碼
<p align=left>...</p>
<p align=center>...</p>
<p align=right>...</p>
復制代碼
給文本加標注:<acronym title="">...</acronym>注釋的內容放在title屬性后的引號中,被注釋的內容放在標簽內。
無序列表:ul,li,有序列表:ol li
定義列表:
<dl>
<dt>...</dt>
<dd>...</dd>
<dt>...</dt>
<dd>...</dd>
</dl>
復制代碼
所以總的來說:jepg可以壓縮圖像的容量,png的質量較好,gif可以做動畫。
矢量圖
說說矢量圖和位圖最大的區別:
無論是否對圖像進行縮放,都不會影響矢量圖的效果,但會影響圖的質量。
設計者一般只愿意將logo,ui圖標,標識符號等簡單圖像存為矢量圖。
圖像的分辨率
分辨率的單位是dpi即每英寸顯示的線數。通常所指的分辨率有兩種,屏幕分辨率和圖片分辨率,屏幕分辨率即計算機顯示器默認的分辨率。
一般目前大部分顯示器的分辨率是1024px x 768px,圖片分辨率定義是用于量度位圖圖像內數據量多少的一個參數。
分辨率越高的圖像,包含的數據越多,圖像的容量就越大,會消耗更多的計算機資源,需要更大的存儲空間。
分辨率指的是每英寸的像素值,通過像素和分辨率的換算可以測算圖片的長度。
頁面中的圖像
<img src=... alt=.../>
復制代碼
<img style="vertial-align:text-top"/>
復制代碼
<img style="vertical-align:middle"/>
復制代碼
<img style="vertical-align:text-bottom"/>
復制代碼
<img style="vertical-alignbaseline"/>
復制代碼
hspace=30px表示圖像左,右兩邊與頁面其他內容隔30px的距離。vspace=30px表示圖像上,下兩邊與頁面的其他內容的間隔距離是30px。
<img src="" widht="" height="">
<img src="..." border=>
<hr align=".." width="..." size="...">
<a href="鏈接對象的路徑">鏈接錨點對象</a>
復制代碼
把郵箱留給需要聯系你的人
<a href="mailto:郵箱地址">鏈接錨點對象</a>
復制代碼
dashed 虛線
double 雙線
groove 槽線
inset 內陷
outset 外陷
復制代碼
熱點圖像區域的鏈接
map標簽:
<map id=...>
<area shape="..." coords="..." href="...">
</map>
復制代碼
shape屬性,用于確定選區的形狀,rect矩形,circle圓形,poly多邊形。href屬性,就是超鏈接。coords屬性,用于控制形狀的位置,通過坐標來找到這個位置。
計算矩形的面積
<html>
<head>
<title>計算矩形的面積</title>
<style type="text/css">
.result {font-weight:bold;}
</style>
<script language="JavaScript">
function calculate() {
var length=document.data.length.value;
var width=document.data.width.value;
var height=document.data.height.value;
var area=document.getElementById('area');
area.innerHTML=length*widht;
volume.innerHTML=length*widht*height;
}
</script>
復制代碼
<form action="my.php"></form>
復制代碼
它表明了這是一個表單,其作用是提交my.php頁面中的數據。
<form name="data">
復制代碼
原理:在客戶端接收用戶的信息,然后將數據遞交給后臺的程序來操控這些數據。
<script language="JavaScript">
復制代碼
如果通過引用外部javascript程序,就像鏈接外聯樣式:
<script type="text/javascript" src="dada.js"></script>
復制代碼
<form action="da.php"></form>
復制代碼
提交方式用get,表單域中輸入的內容會添加在action指定的url中,當表單提交之后,用戶會獲取一個明確的url。get在安全性上較差,所有表單域的值直接呈現。post除了有可見的處理腳本程序,別的東西都可以隱藏。
<form name="dada">
復制代碼
<form action="mailto:da@qq.com" method="post" name="dada"
enctype="text/plain" target="_blank"></form>
復制代碼
是指用戶輸入數據的地方,表單域可分為3個對象,input, textarea, select。
input對象下的多種表單的表現形式。
<input name="" type="" value="" size="" maxlength="">
復制代碼
<form action="" method="post">
<input name="name" type="text" size="20" maxlength="12">
</form>
<input name="secret" type="password" size="20" maxlength="20">
<input name="one" type="radio" value="one" checked="checked">
<input name="one" type="radio" value="two">
<input type="submit" value="確定">
<input type="reset" value="恢復">
復制代碼
創建submit按鈕或reset按鈕時,name屬性不是必需的。
使用hidden來記錄頁面的數據并將它隱藏起來,用戶對這些數據通常并不關心,但是必須提交數據。
<form action=da.asp>
<input type=hidden name=somehidden value=dada>
<input type=submit value=下一頁>
</form>
復制代碼
image樣式的表單
<input type="image" src="圖片/小圖標.jpg" alt="確定">
復制代碼
file上傳文件的樣式表單
file樣式表單允許用戶上傳自己的文件
<html>
<head>
<title>file樣式的表單</title>
<style type="text/css">
body {font:120% 微軟雅黑;}
input {font:100% 微軟雅黑;}
</style>
</head>
上傳我的文件:
<form action="..." method="post" enctype="multipart/form-data">
<input type="file" name="uploadfile" id="uploadfile"/>
</form>
</body>
</html>
復制代碼
textarea對象的表單
textarea對象的表單
<html>
<head>
<title>file樣式的表單</title>
<style type="text/css">
body{font:120% 微軟雅黑;}
textarea{font:80% 微軟雅黑;color:navy;}
</style>
</head>
<body>
留言板
<form action="..." method="post" enctype="multipart/form-data">
<textarea name="dada" rows="10" cols="50" value="dada">請說:</textarea>
</form>
</body>
</html>
復制代碼
select對象的表單
select對象的表單
<form action="">
地址:
<select name="da1">
<option>1</option>
</select>
</form>
復制代碼
使用optgroup標簽配合label屬性來給選項分類:
<select name="上海">
<optgroup label="da1">
<option>1</option>
</optgroup>
<optgroup label="da2">
<option>2</option>
</optgroup>
</select>
復制代碼
在select標簽中加入size屬性即可,如size=6表示是一個能容納6行文字的文本框,超出設置的行數時,將出現滾動條。
<select name="上海" size="6">
復制代碼
表單域集合:表單域的代碼由fieldset標簽和legend標簽組合而成。
<form action="..." method="post">
<fieldset>
<legend>注冊信息:</legend>
輸入用戶名:<input name="name" type="text" size="20" maxlength="12">
</fieldset>
</form>
復制代碼
表單輸入類型
<input type="url" name="webUrl" id="webUrl" value="http://wwwxxx"/>
復制代碼
<input type="email" name="dada" id="dada" value="23@qq.com"/>
復制代碼
<input type="range" name="volume" id="volume" min="0" max="1" step="0.2"/>
復制代碼
<input type="number" name="score" id="score" min="0" max="10" step="0.5"/>
復制代碼
增加表單的特性以及元素
<input name="name" type="text" form="form1" required/>
<form id="form1">
<input type="submit" value="提交"/>
</form>
復制代碼
<form id="form1" method="post">
<input name="name" type="text" form="form1"/>
<input type="submit" value="提交到page1" formaction="?page=1”/>
<input type="submit" value="提交到page2" formaction="?page=2"/>
<input type="submit" value="提交"/>
</form>
復制代碼
placeholder特性
<input name="name" type="text" placeholder="請輸入關鍵詞"/>
復制代碼
autofocus特性:用于當頁面加載完成時,可自動獲取焦點,每個頁面只允許出現一個有autofocus特性的input元素。
<input name="key" type="text" autofocus/>
復制代碼
autocomplete特性用于form元素和輸入型的Input元素,用于表單的自動完成。
input name="key" type="text" autocommplete="on"/>
復制代碼
autocomplete特性有三個值,可以指定"on","off"和""不指定,不指定就將使用瀏覽器的默認設置。
<input name="email" type="email" list="emaillist"/>
<datalist id="emaillist">
<option value="23#qq.com">xxxx</option>
</datalist>
復制代碼
keygen元素提供一個安全的方式來驗證用戶。
<form action="">
<input type="text" name="name"/><br>
<keygen name="security"/>
<br><input type="submit"/>
</form>
復制代碼
<form oninput="x.value=dada.value">
<input type="range" name="volume" value="50"/>
<output name="x"></output>
</form>
復制代碼
為某個表單內部的元素設置了required特性,那么這項的值不能為空,否則無法提交表單。
<input name="name" type="text" placeholder="dada" required/>
復制代碼
<input name="code" type="text" value="" pattern="[0-9]{6}" placeholder="da"/>
復制代碼
<input type="range" name="dada" id="dada" min="0" max="1" step="0.2"/>
復制代碼
<form action="dada.asp" novalidate="novalidate">
<input type="email" name="user_email"/>
<input type="submit"/>
</form>
復制代碼
var validityState=document.getElementById("username").validity;
復制代碼
var willValidate=document.getElementById("username").willValidate;
復制代碼
var validationMessage=document.getElementById("username").validationMessage;
復制代碼
好了各位,以上就是這篇文章的全部內容,能看到這里的人都是人才。我后面會不斷更新技術相關的文章,如果覺得文章對你有用,歡迎給個“贊”,也歡迎分享,感謝大家 !!
TML 5對表單新增了很多功能和屬性,可以更加方便地進行表單開發,form屬性代碼如下:
所以在HTML 5中定義了表單的從屬關系,而不再完全依賴form的位置。在下面的代碼中,輸入框txtPhone是屬于表單myForm的,因此可以提交輸入框內容。在之前的HTML中這種寫法是不能提交txtPhone輸入框的內容的。 placeholder屬性一般用于文本輸入框上,其主要作用是當文本框處于未輸入的狀態并且內容為空時,顯示一段提示文本內容。
<input type="text" id="txtUserName" class="form-control" placeholder="請輸入用戶名"/>
這個屬性達到的效果是我們常見的水印效果,如圖所示。
autofocus屬性的作用是指定控件自動活動的焦點,一個頁面中只能有一個控件具有該屬性。
HTML 5允許單行文本框中使用list屬性,配合datalist元素一起使用。list主要用于提示文本框輸入,datalist用于提供數據源,目前支持該特性的瀏覽器較少。
Autocomplete屬性用于完成自動輸入的功能,有兩個值;on和off,分別代表自動完成輸入和禁止自動完成輸入。很多瀏覽器不支持該屬性,但是自動完成插件是筆者平時使用較多的。
Required屬性是用作必填屬性,如果使用到了這個屬性則當表單中的元素為空時無法提交,此屬性作用于輸入框元素上。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。