近看了一些關于JavaScript的測試腳本,覺得JS 中問號的用法還是蠻有意思的,于是做了一下總結,在這里分享給大家!JS中的問號大概有三種用法,分別是:空值合并操作符、可選鏈操作符和三目運算。
空值合并操作符??是一個邏輯操作符,當左側的操作數為 null 或者 undefined 時,返回其右側操作數,否則返回左側操作數。
例如
console.log(null ?? "xx")
輸出 xx
console.log(1 ?? "xx")
輸出 1
可選鏈操作符(?.)可選鏈操作符允許讀取位于連接對象鏈深處的屬性的值,而不必明確驗證鏈中的每個引用是否有效。 使用它的好處是引用為null 或者 undefined的情況下不會引起錯誤。
語法:obj?.prop obj?.[expr] arr?.[index] func?.(args)
例如
var obj={a:{b:1}}
console.log(obj?.a?.b)
輸出1
console.log(obj?.a?.c)
輸出 undefined
這是三目運算,具體表達式是(condition ? exprIfTrue : exprIfFalse)
該表達式的含義是 條件condition是真,則執行exprIfTrue ,否則執行exprIfFalse
舉個例子大家就懂了
var n = 10;
console.log((n >= 11) ? "a" : "b");
輸出b
當 var n = 12;
輸出a
如果您還知道哪些JS 中關于問號的特殊用法歡迎留言討論。如果文章幫到了您,勞煩點贊轉發!
事件高頻觸發后,n秒內函數只會執行一次,若n秒內事件再次觸發,則重新計時,總之就是要等觸發完事件 n 秒內不再觸發事件,函數才執行
function debounce(callback, wait) {
let timer
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => {
callback.call(this,args)
},wait)
}
}
// 使用
document.body.addEventListener('mousemove',debounce((e)=>{
console.log(this,e,'mousemove-debounce')
},1000))
如果事件持續觸發,在指定時間內,只執行一次事件
時間戳方式
// 時間戳方式
/**
使用時間戳,當觸發事件的時候,我們取出當前的時間戳,然后減去之前的時間戳(最一開始值設為 0 ),
如果大于設置的時間周期,就執行函數,然后更新時間戳為當前的時間戳,如果小于,就不執行。
*/
function throttle(callback, wait) {
let start = 0
return function(...args) {
const now = +new Date()
if(now-start >= wait ) {
callback.call(this,args)
start = now
}
}
}
//使用
const cb = throttle(function(e){
console.log(this)
},1000)
document.body.addEventListener('mousemove',()=>{
cb.call({name:'張三'})
},1000)
// {name: '張三'}
定時器方式
// 定時器方式
/**
*
* 當觸發事件的時候,我們設置一個定時器,再觸發事件的時候,如果定時器存在,就不執行,
* 直到定時器執行,然后執行函數,清空定時器。
*/
function throttle(callback, wait) {
let timer
return function(...args) {
if(!timer) {
timer = setTimeout(()=>{
timer = null
callback.call(this,args)
},wait)
}
}
}
const cb = throttle(function(e){
console.log(this)
},1000)
document.body.addEventListener('mousemove',()=>{
cb.call({name:'張三'})
},1000)
// {name: '張三'}
new 運算符創建一個用戶定義的對象類型的實例或具有構造函數的內置對象的實例。
function myNew() {
// 1.新建一個空對象
let obj = {}
// 2.獲得構造函數
let con = [].shift.call(arguments)
// 3.鏈接原型,實例的 __proto__ 屬性指向構造函數的 prototype
obj.__proto__ = con.prototype
// 4.綁定this,執行構造函數
let res = con.apply(obj, arguments)
// 5.返回新對象
return typeof res === 'object' ? res : obj
}
function Person(name) {
this.name = name
}
let person = myNew(Person,'nanjiu')
console.log(person) //{name: "nanjiu"}
console.log(typeof person === 'object') //true
console.log(person instanceof Person) // true
instanceof 用于檢測構造函數的prototype是否在實例的原型鏈上,需要注意的是instanceof只能用來檢測引用數據類型,對于基本數據檢測都會返回false
通過循環檢測實例的__proto__屬性是否與構造函數的prototype屬性相等
/**
* instanceof 用于檢測構造函數的prototype是否在實例的原型鏈上
*/
function myInstanceof(left, right) {
// 先排除基本數據類型
if(typeof left !== 'object' || left === null) return false
let proto = left.__proto__
while(proto) {
if(proto === right.prototype) return true
proto = proto.__proto__
}
return false
}
function Person() {}
let person = new Person()
console.log(myInstanceof(person,Person)) // true
apply() 方法調用一個具有給定this值的函數,以及以一個數組(或類數組對象)的形式提供的參數。
Function.prototype.myApply = function(context) {
var context = context || window // 獲取需要綁定的this
context.fn = this // 獲取需要改變this的函數
const arg = arguments[1] // 獲取傳遞給函數的參數
if(!(arg instanceof Array)) {
throw Error('參數需要是一個數組')
}
const res = context.fn(...arg) // 執行函數
delete context.fn // 刪除該方法
return res // 返回函數返回值
}
function say(a,b,c) {
console.log(this.name,a,b,c)
}
say.myApply({name:'nanjiu'},[1,2,3]) //nanjiu 1 2 3
say.apply({name:'nanjiu'},[1,2,3]) //nanjiu 1 2 3
call() 方法使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數。
Function.prototype.myCall = function(context) {
var context = context || window // 獲取需要改變的this
context.fn = this // 獲取需要改變this的函數
const args = [...arguments].slice(1) // 獲取參數列表
const res = context.fn(...args) // 將參數傳給函數并執行
delete context.fn // 刪除該方法
return res // 返回函數返回值
}
function say(a,b,c) {
console.log(this.name,a,b,c)
}
say.myCall({name:'nanjiu'},1,2,3) //nanjiu 1 2 3
say.call({name:'nanjiu'},1,2,3) //nanjiu 1 2 3
bind() 方法創建一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定為 bind() 的第一個參數,而其余參數將作為新函數的參數,供調用時使用。
Function.prototype.myBind = function(context) {
var context = context || window //獲取需要改變的this
context.fn = this // 獲取需要改變this的函數
//獲取函數參數
const args = [...arguments].slice(1)
// 與apply,call不同的是這里需要返回一個函數
return () => {
return context.fn.apply(context,[...args])
}
}
function say(a,b,c) {
console.log(this.name,a,b,c)
}
say.bind({name: 'nanjiu'},1,2,3)() //nanjiu 1 2 3
say.myBind({name: 'nanjiu'},1,2,3)() //nanjiu 1 2 3
forEach() 方法對數組的每個元素執行一次給定的函數,無返回值。
arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
Array.prototype.myForEach = function(callback, context) {
const arr = this // 獲取調用的數組
const len = arr.length || 0
let index = 0 // 數組下標
while(index < len) {
callback.call(context ,arr[index], index)
index++
}
}
let arr = [1,2,3]
arr.forEach((item,index) => {
console.log(`key: ${index} - item: ${item}`)
})
console.log('----------')
arr.myForEach((item,index) => {
console.log(`key: ${index} - item: ${item}`)
})
/**
* key: 0 - item: 1
key: 1 - item: 2
key: 2 - item: 3
----------
key: 0 - item: 1
key: 1 - item: 2
key: 2 - item: 3
*/
map() 方法創建一個新數組,其結果是該數組中的每個元素是調用一次提供的函數后的返回值。
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])
callback
生成新數組元素的函數,使用三個參數:
thisArg可選
執行 callback 函數時值被用作this。
/**
* map() 方法創建一個新數組,其結果是該數組中的每個元素是調用一次提供的函數后的返回值。
*/
Array.prototype.myMap = function(callback, context) {
const arr = this,res = []
const len = arr.length || 0
let index = 0
while(index < len) {
res.push(callback.call(context, arr[index], index))
index ++
}
return res // 與forEach不同的是map有返回值
}
const arr = [1,2,3]
let res1 = arr.map((item,index) => {
return `k:${index}-v:${item}`
})
let res2 = arr.myMap((item,index) => {
return `k:${index}-v:${item}`
})
console.log(res1) // [ 'k:0-v:1', 'k:1-v:2', 'k:2-v:3' ]
console.log(res2) // [ 'k:0-v:1', 'k:1-v:2', 'k:2-v:3' ]
filter() 方法創建一個新數組, 其包含通過所提供函數實現的測試的所有元素。
var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
callback
用來測試數組的每個元素的函數。返回 true 表示該元素通過測試,保留該元素,false 則不保留。它接受以下三個參數:
thisArg可選
執行 callback 時,用于 this 的值。
/**
* `filter()` 方法創建一個新數組, 其包含通過所提供函數實現的測試的所有元素。
*/
Array.prototype.myFilter = function(callback, context) {
const arr = this,res = []
const len = arr.length
let index = 0
while(index < len) {
if(callback.call(context,arr[index],index)) {
res.push(arr[index])
}
index ++
}
return res
}
const arr = [1,2,3]
let res1 = arr.filter((item,index) => {
return item<3
})
let res2 = arr.myFilter((item,index) => {
return item<3
})
console.log(res1) // [ 1, 2 ]
console.log(res2) // [ 1, 2 ]
柯里化,英語:Currying(果然是滿滿的英譯中的既視感),是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,并且返回接受余下的參數而且返回結果的新函數的技術。
先來理解一下什么是函數柯里化,上面文縐縐的內容可能不是那么容易理解,我們還是直接上代碼來理解吧
// 假如有這樣一個函數
function add (a,b,c) {
console.log(a+b+c)
}
add(1,2,3) //6
/**
* 我們希望可以通過add(1,2)(3)或add(1)(2)(3)或add(1)(2,3)這樣調用也能夠得倒正確的計算結果
這就是函數柯里化的簡單應用
*/
function curry(fn, curArgs) {
const len = fn.length // 需要柯里化函數的參數個數
curArgs = curArgs || []
return function() {
let args = [].slice.call(arguments) // 獲取參數
args = curArgs.concat(args) //拼接參數
// 基本思想就是當拼接完的參數個數與原函數參數個數相等才執行這個函數,否則就遞歸拼接參數
if(args.length < len) {
return curry(fn, args)
}else{
return fn.apply(this, args)
}
}
}
let fn = curry(function(a,b,c){
console.log([a,b,c])
})
fn(1,2,3) // [ 1, 2, 3 ]
fn(1,2)(3) // [ 1, 2, 3 ]
fn(1)(2,3) // [ 1, 2, 3 ]
fn(1)(2)(3) // [ 1, 2, 3 ]
類數組是具有length屬性,但不具有數組原型上的方法。常見的類數組有arguments、DOM操作方法返回的結果。
function translateArray() {
//方法一:Array.from
const res1 = Array.from(arguments)
console.log(res1 instanceof Array, res1) // true [ 1, 2, 3 ]
// 方法二:Array.prototype.slice.call
const res2 = Array.prototype.slice.call(arguments)
console.log(res2 instanceof Array, res2) // true [ 1, 2, 3 ]
// 方法三:concate
const res3 = [].concat.apply([],arguments)
console.log(res3 instanceof Array, res3) // true [ 1, 2, 3 ]
// 方法四:擴展運算符
const res4 = [...arguments]
console.log(res4 instanceof Array, res4) // true [ 1, 2, 3 ]
}
translateArray(1,2,3)
在拷貝的時候判斷一下屬性值的類型,如果是對象,遞歸調用深拷貝函數
/**
* 在拷貝的時候判斷一下屬性值的類型,如果是對象,遞歸調用深拷貝函數
*/
function deepClone(obj, cache=new Map()) {
// 基本數據類型直接返回
if(typeof obj !== 'object' || obj === null) return obj
// 防止循環引用
const cacheTarget = cache.get(obj)
// 已經存在就直接返回
if(cacheTarget) return cacheTarget
let newObj = obj instanceof Array ? [] : {} // 新建一個對象
cache.set(obj, newObj)
// 遍歷原對象
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
}
}
return newObj
}
const obj = {
name: '張三'
}
const obj1 = obj
const obj2 = deepClone(obj)
console.log(obj1===obj) //true
console.log(obj2===obj) //false
原型鏈繼承實現的原理就是將構造函數的原型設置為另一個構造函數的實例對象,這樣就可以繼承另一個原型對象的所有屬性和方法,可以繼續往上,最終形成原型鏈。
function Parent1(name, age) {
this.name = name,
this.age = age
}
Parent1.prototype.say = function() {
console.log(this.name)
}
function Child1(name) {
this.name = name
}
Child1.prototype = new Parent1()
Child1.prototype.constructor = Child1
let child1 = new Child1('誠實',18)
console.log(child1) //Child1 {name: '誠實'}
child1.say() // 誠實
缺點:
為了解決原型中包含引用類型值的問題,開始使用借用構造函數,也叫偽造對象或經典繼承
構造函數繼承實現的原理就是在子類中調用父類構造函數來實現繼承
function Parent2(age) {
this.age = age
this.say = function() {
console.log(this.name)
}
}
function Child2(name,age,gender) {
this.name = name
Parent2.call(this, age)
this.gender = gender
}
let child2 = new Child2('張三', 18, 'boy')
console.log(child2) //Child2 {name: '張三', age: 18, gender: 'boy'}
child2.say() // 張三
優點: 可以傳遞參數以及避免了引用類型的屬性被所有實例共享
缺點: 所有方法都在構造函數內,每次創建對象都會創建對應的方法,大大浪費內存
也叫偽經典繼承,將原型鏈和借用構造函數的技術組合到一塊。使用原型鏈實現對原型屬性和方法的繼承,而通過構造函數來實現對實例屬性的繼承。
function Parent3(age) {
this.age = age
}
Parent3.prototype.say = function() {
console.log(this.name)
}
function Child3(name,age,gender) {
this.name = name
Parent3.call(this, age)
this.gender = gender
}
Child3.prototype = new Parent3
Child3.prototype.constructor = Child3
let child3 = new Child3('張三', 18, 'boy')
console.log(child3) //Child3 {name: '張三', age: 18, gender: 'boy'}
child2.say() // 張三
借助原型可以基于已有的對象創建新對象,同時還不必因此創建自定義類型。
function object(obj) {
function F(){}
F.prototype = obj
return new F()
}
let parent4 = {
age: 18,
name: '張三',
say() {
console.log(this.name)
}
}
let child4 = object(parent4)
console.log(child4)
/**{[[Prototype]]: Object
age: 18
name: "張三"
say: ? say()
[[Prototype]]: Object
}
*/
child4.say() // 張三
在 object()函數內部,先創建了一個臨時性的構造函數,然后將傳入的對象作為這個構造函數的原型,最后返回了這個臨時類型的一個新實例。從本質上講,object()對傳入其中的對象執行了一次淺復制
它其實就是ES5 Object.create的模擬實現,將傳入的對象作為創建對象的原型
在只想讓一個對象與另一個對象保持類似的情況下,原型繼承是完全可以勝任的。原型模式下的缺點:引用類型屬性的共享問題。
寄生式繼承與原型式繼承緊密相關,與寄生構造函數和工廠模式類似,即創建一個僅用于封裝繼承過程的函數,該函數在內部以某種方式來增強對象,最后再返回對象。
function createAnother(original) {
var clone = object(original) //通過調用函數創建一個新對象
clone.say = function(){ // 以某種方式來增強這個對象
console.log('nanjiu')
};
return clone // 返回這個對象
}
缺點: 跟借用構造函數模式一樣,每次創建對象都會創建一遍方法。
組合繼承是 JavaScript 最常用的繼承模式;不過,它也有自己的不足。組合繼承最大的問題就是無論什么情況下,都會調用兩次超類型構造函數:一次是在創建子類型原型的時候,另一次是在子類型構造函數內部。沒錯,子類型最終會包含超類型對象的全部實例屬性,但我們不得不在調用子類型構造函數時重寫這些屬性。
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function prototype(child, parent) {
var prototype = object(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
function Parent6(age) {
this.age = age
}
Parent6.prototype.say = function() {
console.log(this.name)
}
function Child6(name, gender) {
this.name = name
this.gender = gender
}
// 使用
prototype(Child6, Parent6);
let child6 = new Child6('nanjiu', 'boy')
console.log(child6) // Child6 {name: 'nanjiu', gender: 'boy'}
child6.say() // nanjiu
JavaScript 主要通過原型鏈實現繼承。原型鏈的構建是通過將一個類型的實例賦值給另一個構造函數的原型實現的。這樣,子類型就能夠訪問超類型的所有屬性和方法,這一點與基于類的繼承很相似。
步驟:
function ajax(url, method, data=null) {
const xhr = XMLHttpRequest() // 咱們這里就不管IE低版本了
// open()方法,它接受3個參數:要發送的請求的類型,請求的url和是否異步發送請求的布爾值。
xhr.open(method, url ,false) // 開啟一個請求,當前還未發送
xhr.onreadyStatechange = function() {
if(xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
console.log("Request was unsuccessful: " + xhr.status);
}
}
}
if(method === 'post') {
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
}
xhr.send(data) // get請求,data應為null,參數拼接在URL上
}
function flat(arr) {
const res = []
// 遞歸實現
const stack = [...arr] // 復制一份
while(stack.length) {
//取出復制棧內第一個元素
const val = stack.shift()
if(Array.isArray(val)) {
// 如果是數組,就展開推入棧的最后
stack.push(...val)
}else{
// 否則就推入res返回值
res.push(val)
}
}
return res
}
const arr = [1,[2],[3,4,[5,6,[7,[8]]]]]
console.log(flat(arr)) //[1, 2, 3, 4, 5, 6, 7, 8]
當然你也可以用數組自帶的方法flat,將展開層數指定為Infinity無窮大,看看面試官搭不搭理你
const arr = [1,[2],[3,4,[5,6,[7,[8]]]]]
console.log(arr.flat(Infinity)) //[1, 2, 3, 4, 5, 6, 7, 8]
思路就是遞歸調用setTimeout
function mySetInterval(callback, delay) {
let timer = null
let interval = () => {
timer = setTimeout(()=>{
callback()
interval() // 遞歸
}, delay)
}
interval() // 先執行一次
return {
id: timer,
clear: () => {
clearTimeout(timer)
}
}
}
let time = mySetInterval(()=>{
console.log(1)
},1000)
setTimeout(()=>{
time.clear()
},2000)
思路就是setInterval執行一次后將setInterval清除即可
function mySetTimeout(callback, delay) {
let timer = null
timer = setInterval(()=>{
callback()
clearInterval(timer)
},delay)
}
mySetTimeout(()=>{
console.log(1)
},1000)
實現一個函數,n秒后執行指定函數
function sleep(func, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(func())
}, delay)
})
}
function say(name) {
console.log(name)
}
async function go() {
await sleep(()=>say('nanjiu'),1000) //過一秒打印nanjiu
await sleep(()=>say('前端張三'),2000) // 再過兩秒打印前端張三
}
go()
let arr = [1,2,3,2,4,5,3,6,2]
function arrayToHeavy1(arr) {
return [...new Set(arr)]
}
console.log(arrayToHeavy1(arr)) //[ 1, 2, 3, 4, 5, 6 ]
function arrayToHeavy2(arr) {
let newArr = []
for(let i=0; i<arr.length; i++) {
if(newArr.indexOf(arr[i]) == -1){
newArr.push(arr[i])
}
}
return newArr
}
console.log(arrayToHeavy2(arr)) //[ 1, 2, 3, 4, 5, 6 ]
function arrayToHeavy3(arr) {
return arr.filter((item, index) => {
return arr.indexOf(item) == index
})
}
console.log(arrayToHeavy3(arr)) //[ 1, 2, 3, 4, 5, 6 ]
function arrayToHeavy4(arr) {
let map = new Map()
for(let i=0; i<arr.length; i++) {
if(!map.has(arr[i])){
map.set(arr[i], 1)
}
}
return [...map.keys()]
}
console.log(arrayToHeavy4(arr)) //[ 1, 2, 3, 4, 5, 6 ]
function arrayToHeavy5(arr) {
let res = []
for(let i=0; i<arr.length; i++) {
if(!res.includes(arr[i])) {
res.push(arr[i])
}
}
return res
}
console.log(arrayToHeavy5(arr)) //[ 1, 2, 3, 4, 5, 6 ]
根據key獲取URL上的參數值
function queryData(key) {
let url = window.location.href,obj = {}
let str = url.split('?')[1] // 先拿到問號后面的所有參數
let arr = str.split('&') // 分割參數
for(let i=0; i< arr.length; i++) {
let kv = arr[i].split('=')
obj[kv[0]] = decodeURIComponent(kv[1])
}
console.log(url,obj)
// {a: '1', b: '2', c: '3', name: '張三'}
return obj[key]
}
//http://127.0.0.1:5500/src/js/2022/%E6%89%8B%E5%86%99/index.html?a=1&b=2&c=3&name=%E5%8D%97%E7%8E%96
console.log(queryData('name')) // 張三
F(n) = F(n - 1) + F(n - 2),其中 n > 1
function fib(n) {
if(n == 0) return 0
if(n == 1 || n == 2) return 1
return fib(n-1) + fib(n-2)
}
// console.log(fib(4)) //F(4)=F(3)+F(2)=F(2)+F(1)+F(2)=1+1+1=3
let t = +new Date()
console.log(fib(40)) //102334155
console.log(+new Date()-t) //783ms
function fib2(n) {
if(fib2[n] !== undefined) return fib2[n]
if(n == 0) return 0
if(n == 1 || n == 2) return 1
const res = fib2(n-1) + fib2(n-2)
fib2[n] = res
return res
}
let t1 = +new Date()
console.log(fib2(40)) //102334155
console.log(+new Date()-t1) //5ms
用過Vue的eventBus的同學應該很熟悉,$on訂閱事件,$emit發布事件
歡web前端網頁開發和python開發的、請加下企鵝群:526929231 內有大量案例和學習教程,對python、和web感興趣的朋友可以加下哦
所有的控件(表單元素都寫在form表單標簽中)
<form> 使用form標簽建立表單域,當提交數據的時候會收集表單域里面的數據然后發送給服務器</form>
賦予不同的type值可實現不同的表單控件
type類型 | 描述 |
---|---|
text | 文本輸入框 maxlength最大長度、onlyready只讀、 disabled禁止、 placeholder |
password | 密碼遮掩框 |
radio | 單選按鈕,checked默認選擇 |
checkbox | 多選框 |
submit | 收集表單域的name數據,然后提交到服務器上 |
<select> <option value="music">聽音樂</option> <option value="running">跑步</option> <option value="study">學習</option> <option value="coffee">找小姐姐一起喝咖啡</option></select><!-- selected="selected"默認選中 --><!-- size="2" 現實兩行下拉項 --><!-- disabled 禁止選擇 -->
selected默認選擇一項
<textare cols="30" rows="10"></textarea>
clos顯示多少列,rows現實多少行
單獨使用沒有效果,一般配合js點擊按鈕的時候執行什么操作
<input type="button" value="自定義按鈕標題" />
回到表單初識狀態
<input type="reset" value="重置表單" />
目的在于收集或發送信息 頁面上面沒有任何效果 CSRF跨域攻擊在此作用
<input type="hidden" value="ABCD1234" />
為input元素定義一個標記,label元素不會向用戶呈現任何特殊效果。不過,它為鼠標用戶改進了可用性。如果你在label元素內點擊文本,就會觸發此控件。就是說,當用戶選擇該標簽是,瀏覽器就會自動將焦點轉到和標簽相關的表單控件上
<input id="man" type="radio" /><label for="man">男</label>
屬性 | 描述 |
---|---|
action | 指定提交到哪個url上 |
method | 提交方式,常用的GET / POST |
Method | Description |
---|---|
GET | URL地址欄上做拼接問號再加參數 |
POST | 隱式提交方式,看不到,可以抓包 |
pre可定義預格式化的文本。
pre元素中的文本通常會保留空格和換行符。
主要用于在網頁上顯示代碼,比如在網頁當中顯示html模板
*請認真填寫需求信息,我們會在24小時內與您取得聯系。