基本類型(值類型)javascript
引用類型html
var name = 'jie'; name.toUpperCase(); console.log(name); //jie
基本數據類型直接存儲在棧(stack)中的簡單數據段java
爲何放入棧中存儲
var a = 1; var b = true; console.log(a == b) //true console.log(a === b); //false
var a = { age: 20 } var b = a; b.age = 21; console.log(a.age) //21 console.log(b.age) //21 console.log(a === b) //true
var a = { age: 20 } var b = a; b.age = 21; console.log(a.age) //21 console.log(b.age) //21
變量a初始化時,a指針指向對象{age:20}的地址,a賦值給b後,b又指向該對象{age:20}的地址,這兩個變量指向了同一個對象。所以,改變其中任何一個變量,都會相互影響。node
若是取消某一個變量對於原對象的引用,不會影響到另外一個變量git
var a = { age: 20 } var b = a; a = 1; console.log(a) //1 console.log(b) //{age:20}
a和b指向同一個對象,而後a的值變爲1,這時不會對b產生影響,b仍是指向原來的那個對象。es6
function test(person) { person.age = 26 person = { name: 'hzj', age: 18 } return person } const p1 = { name: 'fyq', age: 19 } const p2 = test(p1) console.log(p1) // -> ? console.log(p2) // -> ?
p1:{name: 「fyq」, age: 26} p2:{name: 「hzj」, age: 18}
typeof Symbol(); // symbol 有效 typeof ''; // string 有效 typeof 1; // number 有效 typeof true; //boolean 有效 typeof undefined; //undefined 有效 typeof new Function(); // function 有效 typeof null; //object 無效 typeof [] ; //object 無效 typeof new Date(); //object 無效 typeof new RegExp(); //object 無效
[] instanceof Array; //true {} instanceof Object;//true new Date() instanceof Date;//true new RegExp() instanceof RegExp//true
null 和 undefined 是無效的對象,所以是不會有 constructor 存在的,這兩種類型的數據須要經過其餘方式來判斷github
Object.prototype.toString.call('') ; // [object String] Object.prototype.toString.call(1) ; // [object Number] Object.prototype.toString.call(true) ; // [object Boolean] Object.prototype.toString.call(undefined) ; // [object Undefined] Object.prototype.toString.call(null) ; // [object Null] Object.prototype.toString.call(new Function()) ; // [object Function] Object.prototype.toString.call(new Date()) ; // [object Date] Object.prototype.toString.call([]) ; // [object Array] Object.prototype.toString.call(new RegExp()) ; // [object RegExp] Object.prototype.toString.call(new Error()) ; // [object Error] Object.prototype.toString.call(document) ; // [object HTMLDocument] Object.prototype.toString.call(window) ; //[object global] window是全局對象global的引用
https://github.com/ljianshu/B...面試
function fn(){ console.log('aa') } fn()
var fn = function() { console.log('aa') } fn()
var i = 'aa'; (function(i) { console.log(i) //aa })(i)
function fn(i) { (function() { console.log(i) //aa })() } fn('aa')
function fn() { (function(i) { console.log(i) // undefined })() } fn('aa')
function fn() { (function() { console.log(i) })(i) } fn('aa')
函數的做用域分爲全局做用ajax
var a = 'aa' fn() function fn() { var b = 'bb' c = 'cc' console.log(a) //aa console.log(b) //bb console.log(c) //cc } console.log(a) //a console.log(c) //cc console.log(b) //b is not defined
fn() var a = 'aa' function fn() { var b = 'bb' c = 'cc' console.log(a) //aa console.log(b) //bb console.log(c) //cc } console.log(a) //undefined console.log(b) //b is not defined console.log(c) //cc
JavaScript 變量生命週期在它聲明時初始化。json
由於js要實現繼承,js沒有像別的語言有繼承這個東西(es6中的class本質上也是基於原型和原型鏈),
__proto__
原型鏈(隱式原型),每個JavaScript對象(除了 null )都具備的一個屬性,函數也是對象,因此函數也有__proto__function Person() { } var person = new Person(); person.name = 'jie' console.log(person.name)
那什麼是原型呢
每個JavaScript對象(null除外)在建立的時候就會與之關聯另外一個對象,這個對象就是咱們所說的原型,每個對象都會從原型"繼承"屬性
function Person() { } Person.prototype.name = 'jie' var person1 = new Person(); var person2 = new Person(); console.log(person1.name) //jie console.log(person2.name) //jie
用一張圖表示構造函數和實例原型之間
function Person() { } var person = new Person(); console.log(person.__proto__ === Person.prototype) //true
用一張圖表示實例和實例原型之間的__proto__
每一個原型都有一個 constructor 屬性指向關聯的構造函數
function Person() { } console.log(Person === Person.prototype.constructor) //true
function Person() { } Person.prototype.name = '原型上的名字'; var person1 = new Person(); var person2 = new Person(); person1.name = '實例名字1'; person2.name = '實例名字2'; console.log('person1.name:' + person1.name) //實例名字1 console.log('person2.name:' + person2.name) //實例名字2 delete person1.name; console.log('person1.name:' + person1.name) //原型上的名字 console.log('person2.name:' + person2.name) //實例名字1
原型也是一個對象,既然是對象,咱們就能夠用最原始的方式建立它
var obj = new Object(); obj.name = 'jie' console.log(obj.name)
function Person() { } var p1 = new Person(); console.log(p1.__proto__ === Person.prototype) //true console.log(Person.prototype.__proto__ === Object.prototype) //true console.log(Object.prototype.__proto__ === null) //true
圖中由相互關聯的原型組成的鏈狀結構就是原型鏈,也就是藍色的這條線。
function Person() { } var person = new Person(); console.log(person.constructor === Person); // true
當獲取 person.constructor 時,其實 person 中並無 constructor 屬性,當不能讀取到constructor 屬性時,會從 person 的原型也就是 Person.prototype 中讀取,正好原型中有該屬性,因此:
person.constructor === Person.prototype.constructor
繼承意味着複製操做,然而 JavaScript 默認並不會複製對象的屬性,相反,JavaScript 只是在兩個對象之間建立一個關聯,這樣,一個對象就能夠經過委託訪問另外一個對象的屬性和函數,因此與其叫繼承,委託的說法反而更準確些
https://github.com/mqyqingfen...
JavaScript 採用詞法做用域(lexical scoping),也就是靜態做用域
var value = 1; function fn1() { console.log(value) } function fn2() { var value = 2; fn1() } fn2()
按照函數棧先進後出的順序執行,先執行完fn1(),再執行完fn2()
執行 fn1 函數,先從 fn1 函數內部查找是否有局部變量 value,若是沒有,就根據書寫的位置(),查找上面一層的代碼,也就是 value 等於 1,因此結果會打印 1
使用 var 關鍵詞來聲明變量
var carname;
變量聲明以後,該變量是空的(它沒有值)。
如需向變量賦值,請使用等號:
carname="Volvo";
不過,您也能夠在聲明變量時對其賦值:
var carname="Volvo";
console.log(a)
console.log(a) //undefined var a;
console.log(a) //undefined var a = 10;
var a = 10; console.log(a)
console.log(f1) function f1() { }
console.log(f2) var f2 = function() { }
console.log(this)
執行上下文也叫作執行上下文環境
給執行上下文環境下一個通俗的定義:在執行代碼以前,把將要用到的全部的變量都事先拿出來,有的直接賦值了,有的先用undefined佔個空
這三種數據的準備狀況咱們稱之爲「執行上下文」或者「執行上下文環境」
如下代碼展現了在函數體的語句執行以前,arguments變量和函數的參數都已經被賦值。從這裏能夠看出,函數每被調用一次,都會產生一個新的執行上下文環境。由於不一樣的調用可能就會有不一樣的參數
function fn(x) { console.log(arguments) console.log(x) } fn(10) fn(20)
全局代碼的上下文環境數據內容爲
普通變量(包括函數表達式),如: var a = 10; | 聲明(默認賦值爲undefined) |
---|---|
函數聲明,如: function fn() { } | 賦值 |
this | 賦值 |
若是代碼段是函數體,那麼在此基礎上須要附加
參數 | 賦值 |
---|---|
arguments | 賦值 |
自由變量的取值做用域 | 賦值 |
http://www.cnblogs.com/wangfu...
執行全局代碼時,會產生一個執行上下文環境,每次調用函數都又會產生執行上下文環境。當函數調用完成時,這個上下文環境以及其中的數據都會被消除,再從新回到全局上下文環境。處於活動狀態的執行上下文環境只有一個
這是一個壓棧出棧的過程——執行上下文棧
function foo() { console.log(this) } var a = 1; foo()
function fn() { console.log(this) } var obj = { fn: fn } obj.fn();
function CreateJsPerson(name, age) { //this是當前類的一個實例p1 this.name = name; //=>p1.name=name this.age = age; //=>p1.age=age console.log(this) } var p1 = new CreateJsPerson("尹華芝", 48);
var age = 99; function PersonX() { this.age = 0; setTimeout(() => { this.age++; console.log(age) //1 }, 1000); } PersonX();
var x = 11; var obj = { x: 22, methods: { x: 33, say: function () { console.log(this.x) }, say2: () => { console.log(this.x) } } } obj.methods.say(); //33 obj.methods.say2(); //11
function add(c, d) { console.log(this) return this.a + this.b + c + d; } var o = { a: 1, b: 3 }; add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
<button id="btn1">箭頭函數this</button> <script> let btn1 = document.getElementById('btn1'); let obj = { name: 'kobe', age: 39, getName: function() { btn1.onclick = () => { console.log(this); //obj }; } }; obj.getName(); </script>
https://github.com/ljianshu/B...
做用域在函數定義時就已經肯定了。而不是在函數調用時肯定
按照程序執行的順序,一步一步把各個上下文環境
http://www.cnblogs.com/wangfu...
在A做用域中使用的變量x,卻沒有在A做用域中聲明(即在其餘做用域中聲明的),對於A做用域來講,x就是一個自由變量
var x = 10; function fn() { var b = 20; console.log(x + b) //這裏的x在這裏就是一個自由變量 }
而取x的值時,就須要到另外一個做用域中取。到哪一個做用域中取呢?
要到建立這個函數的那個做用域中取值——是「建立」,而不是「調用」,切記切記
var x = 10; function fn() { console.log(x) //10 } function show(f) { var x = 20; (function() { f() })() } show(fn)
若是跨了一步,還沒找到呢?——接着跨!——一直跨到全局做用域爲止。要是在全局做用域中都沒有找到,那就是真的沒有了。
這個一步一步「跨」的路線,咱們稱之爲——做用域鏈
取自由變量時的這個「做用域鏈」過程:(假設a是自由量)
實例
第13行,fn()返回的是bar函數,賦值給x。執行x(),即執行bar函數代碼。取b的值時,直接在fn做用域取出。取a的值時,試圖在fn做用域取,可是取不到,只能轉向建立fn的那個做用域中去查找,結果找到了。
閉包是指能夠訪問另外一個函數做用域變量的函數,通常是定義在外層函數中的內層函數。
局部變量沒法共享和長久的保存,而全局變量可能形成變量污染,因此咱們但願有一種機制既能夠長久的保存變量又不會形成全局污染。
既想反覆使用,又想避免全局污染
按理說應該銷燬掉fn()的執行上下文環境,可是這裏不能這麼作。注意,重點來了:由於執行fn()時,返回的是一個函數。函數的特別之處在於能夠建立一個獨立的做用域。而正巧合的是,返回的這個函數體中,還有一個自由變量max要引用fn做用域下的fn()上下文環境中的max。所以,這個max不能被銷燬,銷燬了以後bar函數中的max就找不到值了。
所以,這裏的fn()上下文環境不能被銷燬,還依然存在與執行上下文棧中。
建立bar函數是在執行fn()時建立的。fn()早就執行結束了,可是fn()執行上下文環境還存在與棧中,所以bar(15)時,max能夠查找到。若是fn()上下文環境銷燬了,那麼max就找不到了
function fn1() { var n = 0; return function() { console.log(n) } } var result = fn1(); result()
http://www.cnblogs.com/wangfu...
https://zhuanlan.zhihu.com/p/...
http://www.ruanyifeng.com/blo...
https://www.imooc.com/article/68898?block_id=tuijian_wz
function Person(name, age) { this.name = name; this.age = age; this.call = function() { alert(this.name + this.age) } } var p1 = new Person('jie', 12) p1.call()
function myNew3() { let obj = new Object(); //1 let Constructor = [].shift.call(arguments); //2 obj.__proto__ = Constructor.prototype; //3 let result = Constructor.apply(obj, arguments); //4 if (result instanceof Object) { //5 return result } else { return obj; } } var p4 = myNew3(Person, 'wei', 14) p4.call()
https://github.com/mqyqingfen...
var obj = { value: '1' } function fn(name, age) { this.name = name; this.age = age; this.say = function() { alert(this.name + this.age) } } fn.call(obj, 'jie', 10) obj.say()
Function.prototype.call2 = function(context) { var context = context || window; context.fn = this; var args = []; for (var i = 1; i < arguments.length; i++) { args.push(`arguments[${i}]`) } var result = eval(`context.fn(${args})`) delete context.fn; return result; } fn.call2(obj, 'biao', 20) obj.say()
https://www.cnblogs.com/moqiu...
https://github.com/mqyqingfen...
apply與call類型,只是傳參不同
var obj = { value: '1' } function fn(name, age) { this.name = name; this.age = age; this.say = function() { alert(this.name + this.age) } } Function.prototype.apply2 = function(context, arr) { var context = Object(context) || window; context.fn = this; var result; if (!arr) { result = context.fn() } else { var args = []; for (var i = 0; i < arr.length; i++) { args.push(`arr[${i}]`) } result = eval(`context.fn(${args})`) } delete context.fn; return result; } fn.apply(obj, ['jie', 10]) obj.say() fn.apply2(obj, ['biao', 20]) obj.say()
var foo = { value: 1 }; function bar() { console.log(this.value); } // 返回了一個函數 var bindFoo = bar.bind(foo); bindFoo(); // 1
是一個對應於傳遞給函數的參數的類數組對象。
function foo(name, age, sex) { console.log(arguments) } foo('name', 'age', 'sex')
function fn(...arguments) { console.log(arguments) } fn(1, 2, 3)
function Person() { this.name = 'jie' this.color = ['red', 'blue'] } Person.prototype.say = function() { console.log(this.name) } function Student(id) { Person.call(this) this.id = id } Student.prototype = new Person() Student.prototype.constructor = Student var s1 = new Student('1') console.log(s1.name) console.log(s1.color) s1.say() s1.color.push('yellow') console.log(s1.color) var s2 = new Student('2') console.log(s2.name) console.log(s2.color) s2.say() s2.color.push('white') console.log(s2.color)
https://www.cnblogs.com/sarah...
Javascript語言的執行環境是"單線程"(single thread)
所謂"單線程",就是指一次只能完成一件任務。若是有多個任務,就必須排隊,前面一個任務完成,再執行後面一個任務,以此類推。
單線程的優勢
這種模式的好處是實現起來比較簡單,執行環境相對單純
單線程的缺點
壞處是隻要有一個任務耗時很長,後面的任務都必須排隊等着,會拖延整個程序的執行。常見的瀏覽器無響應(假死),每每就是由於某一段Javascript代碼長時間運行(好比死循環),致使整個頁面卡在這個地方,其餘任務沒法執行。
解決單線程的缺點
爲了解決這個問題,Javascript語言將任務的執行模式分紅兩種:同步(Synchronous)和異步(Asynchronous)。
同步模式
後一個任務等待前一個任務結束,而後再執行,程序的執行順序與任務的排列順序是一致的、同步的
異步模式
每個任務有一個或多個回調函數(callback),前一個任務結束後,不是執行後一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行,因此程序的執行順序與任務的排列順序是不一致的、異步的。
異步模式的情景
http請求
$.ajax({ url: '', success: function(data) { console.log(data) }, error: function(error) { console.log(error) } })
Javascript語言的執行環境是"單線程"(single thread)
所謂"單線程",就是指一次只能完成一件任務。若是有多個任務,就必須排隊,前面一個任務完成,再執行後面一個任務,以此類推。
單線程的優勢
這種模式的好處是實現起來比較簡單,執行環境相對單純
單線程的缺點
壞處是隻要有一個任務耗時很長,後面的任務都必須排隊等着,會拖延整個程序的執行。常見的瀏覽器無響應(假死),每每就是由於某一段Javascript代碼長時間運行(好比死循環),致使整個頁面卡在這個地方,其餘任務沒法執行。
解決單線程的缺點
爲了解決這個問題,Javascript語言將任務的執行模式分紅兩種:同步(Synchronous)和異步(Asynchronous)。
同步模式
後一個任務等待前一個任務結束,而後再執行,程序的執行順序與任務的排列順序是一致的、同步的
異步模式
每個任務有一個或多個回調函數(callback),前一個任務結束後,不是執行後一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行,因此程序的執行順序與任務的排列順序是不一致的、異步的。
// 1秒後打印出aa
function fn1(callback) { setTimeout(function() { callback(); }, 1000) } fn1(fn2) function fn2() { console.log('aa') }
ajax(url, () => { // 處理邏輯 ajax(url1, () => { // 處理邏輯 ajax(url2, () => { // 處理邏輯 }) }) })
function ajaxFn(url, callback) { $.ajax({ method: 'get', url: url, success: function(data) { callback(data) }, error: function(error) { console.log(error) } }) } ajaxFn('https://cnodejs.org/api/v1/topic/5433d5e4e737cbe96dcef312', (res) => { console.log(res) console.log('第一個請求完成') ajaxFn('https://cnodejs.org/api/v1/topics', (ress) => { console.log(ress) console.log('第二個請求完成') }) })
function promiseFn(url) { return new Promise((resolve, reject) => { $.ajax({ method: 'get', url: url, success: function(data) { resolve(data) }, error: function(error) { reject(error) } }) }) } promiseFn('https://cnodejs.org/api/v1/topic/5433d5e4e737cbe96dcef312') .then(res => { console.log(res) console.log('第一個請求完成') return promiseFn('https://cnodejs.org/api/v1/topics') }) .then(res => { console.log(res) console.log('第二個請求完成') }) .catch(err => { console.log(err) })
async function asyncFn(url) { return await new Promise((resolve, reject) => { $.ajax({ method: 'get', url: url, success: function(response) { resolve(response); }, error: function(error) { reject(error); } }) }) } async function start() { var result1 = await asyncFn('https://cnodejs.org/api/v1/topic/5433d5e4e737cbe96dcef312'); var result2 = await asyncFn('https://cnodejs.org/api/v1/topics'); console.log(result1) console.log('第一個請求完成') console.log(result2) console.log('第二個請求完成') } start()
在好久以前,科技尚未這麼發達的時候,若是咱們要燒水,須要把水壺放到火爐上,咱們經過觀察水壺內的水的沸騰程度來判斷水有沒有燒開
隨着科技的發展,如今市面上的水壺都有了提醒功能,當咱們把水壺插電以後,水壺水燒開以後會經過聲音提醒咱們水開了。
對於燒水這件事兒來講,傳統水壺的燒水就是同步的,高科技水壺的燒水就是異步的
當你把水放到水壺裏面,按下開關後,你能夠坐在水壺前面,別的事情什麼都不作,一直等着水燒好。你還能夠先去客廳看電視,等着水開就行了。
對於你來講,坐在水壺前面等就是阻塞的,去客廳看電視等着水開就是非阻塞的。
阻塞、非阻塞說的是調用者(使用水壺的我),同步、異步說的是被調用者(水壺)。
var a = 1;//定義了一個number類型 var obj1 = {//定義了一個object類型 name:'obj' };
var a = 1; var b = a; console.log(a) //1 console.log(b) //1 b = 2; console.log(a) //1 console.log(b) //2
賦值的時候,在棧內存中從新開闢內存,存放變量b,因此在棧內存中分別存放着變量a、b各自的值,修改時互不影響
var color1 = ['red', 'blue'] var color2 = color1; console.log(color1) //["red", "blue"] console.log(color2) //["red", "blue"] color1.push('black'); color1.push('yellow'); console.log(color1) // ["red", "blue", "black", "yellow"] console.log(color2) //["red", "blue", "black", "yellow"]
color1與color2指向堆內存中同一地址的同一對象,複製的只是引用地址
所以,對於引用類型的複製,簡單賦值無用,須要拷貝。拷貝存在兩種類型:深拷貝與淺拷貝
淺拷貝只複製指向某個對象的指針,而不復制對象自己,新舊對象仍是共享同一塊內存
修改新對象會改到原對象
var person = { p1: { name: 'jie', age: 18 }, p2: 'biao' } var person1 = person; person1.p1.name = 'nine'; console.log(person) //nine console.log(person1) //nine
深拷貝會另外創造一個如出一轍的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象
修改新對象不會改到原對象
function depClone(obj) { var result = JSON.parse(JSON.stringify(obj)); return result; } var person = { p1: { name: 'jie', age: 18 }, p2: 'biao' } var person2 = depClone(person); person2.p1.name = 'nine'; console.log(person) //jie console.log(person2) //nine
https://www.cnblogs.com/136as...
https://github.com/mqyqingfen...
<button type="button" onclick="fn" id="btn">點我試試</button> <script> function fn() { alert('Hello World'); } </script>
onclick="fn"
要不要加()沒有()
button type="button" onclick="fn" id="btn">點我試試</button> <script> function fn() { alert('Hello World'); } console.log(document.getElementById('btn').onclick); </script>
不彈出框(沒有執行alert())
有()
button type="button" onclick="fn" id="btn">點我試試</button> <script> function fn() { alert('Hello World'); } console.log(document.getElementById('btn').onclick); </script>
彈出框(執行alert())
<button type="button" id="btn">點我試試</button> <script> var btn = document.getElementById('btn'); function fn() { alert('Hello World'); } btn.addEventListener('click', fn, false); // 解綁事件,代碼以下 // btn.removeEventListener('click', fn, false); </script>
假如在一個button上註冊了一個click事件,又在其它父元素div上註冊了一個click事件,那麼當咱們點擊button,是先觸發父元素上的事件,仍是button上的事件呢,這就須要一種約定去規範事件的執行順序,就是事件執行的流程。
瀏覽器在發展的過程當中出現了兩種不一樣的規範
DOM事件模型分爲捕獲和冒泡。一個事件發生後,會在子元素和父元素之間傳播(propagation)。這種傳播分紅三個階段。
捕獲是從上到下,事件先從window對象,而後再到document(對象),而後是html標籤(經過document.documentElement獲取html標籤),而後是body標籤(經過document.body獲取body標籤),而後按照普通的html結構一層一層往下傳,最後到達目標元素。咱們只須要將addEventListener的第三個參數改成true就能夠實現事件捕獲
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> body { margin: 0; } div { border: 1px solid #000; } #grandfather1 { width: 200px; height: 200px; } #parent1 { width: 100px; height: 100px; margin: 0 auto; } #child1 { width: 50px; height: 50px; margin: 0 auto; } </style> </head> <body> <div id="grandfather1"> 爺爺 <div id="parent1"> 父親 <div id="child1">兒子</div> </div> </div> </body> <script> var grandfather1 = document.getElementById('grandfather1'), parent1 = document.getElementById('parent1'), child1 = document.getElementById('child1'); grandfather1.addEventListener('click', function fn1() { console.log('爺爺'); }, true) parent1.addEventListener('click', function fn1() { console.log('爸爸'); }, true) child1.addEventListener('click', function fn1() { console.log('兒子'); }, true) </script> </html>
點擊id爲child1的div標籤時(兒子框),打印的結果是爺爺 => 爸爸 => 兒子,。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> body { margin: 0; } div { border: 1px solid #000; } #grandfather1 { width: 200px; height: 200px; } #parent1 { width: 100px; height: 100px; margin: 0 auto; } #child1 { width: 50px; height: 50px; margin: 0 auto; } </style> </head> <body> <div id="grandfather1"> 爺爺 <div id="parent1"> 父親 <div id="child1">兒子</div> </div> </div> </body> <script> var grandfather1 = document.getElementById('grandfather1'), parent1 = document.getElementById('parent1'), child1 = document.getElementById('child1'); grandfather1.addEventListener('click', function fn1() { console.log('爺爺'); }, false) parent1.addEventListener('click', function fn1() { console.log('爸爸'); }, false) child1.addEventListener('click', function fn1() { console.log('兒子'); }, false) </script> </html>
點擊id爲child1的div標籤時(兒子框),打印的結果是兒子=>爸爸=>爺爺
因爲事件會在冒泡階段向上傳播到父節點,所以能夠把子節點的監聽函數定義在父節點上,由父節點的監聽函數統一處理多個子元素的事件。這種方法叫作事件的代理(delegation)。
舉個例子,好比一個宿舍的同窗同時快遞到了,一種方法就是他們都傻傻地一個個去領取,還有一種方法就是把這件事情委託給宿舍長,讓一我的出去拿好全部快遞,而後再根據收件人一一分發給每一個宿舍同窗;
在這裏,取快遞就是一個事件,每一個同窗指的是須要響應事件的 DOM 元素,而出去統一領取快遞的宿舍長就是代理的元素,因此真正綁定事件的是這個元素,按照收件人分發快遞的過程就是在事件執行中,須要判斷當前響應的事件應該匹配到被代理元素中的哪個或者哪幾個。
那麼利用事件冒泡或捕獲的機制,咱們能夠對事件綁定作一些優化。 在JS中,若是咱們註冊的事件愈來愈多,頁面的性能就愈來愈差,由於:
<ul id="list"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> </ul> <script> // 給父層元素綁定事件 document.getElementById('list').addEventListener('click', function(e) { // 兼容性處理 var event = e || window.event; var target = event.target || event.srcElement; // 判斷是否匹配目標元素 if (target.nodeName.toLocaleLowerCase() === 'li') { console.log('the content is: ', target.innerHTML); } }); </script>
這是常規的實現事件委託的方法,可是這種方法有BUG,當監聽的元素裏存在子元素時,那麼咱們點擊這個子元素事件會失效,因此咱們能夠聯繫文章上一小節說到的冒泡事件傳播機制來解決這個bug。改進的事件委託代碼:
<ul id="list"> <li>1 <span>aaaaa</span></li> <li>2 <span>aaaaa</span></li> <li>3 <span>aaaaa</span></li> <li>4</li> </ul> <script> document.getElementById('list').addEventListener('click', function(e) { // 兼容性處理 var event = e || window.event; var target = event.target || event.srcElement; // 判斷是否匹配目標元素 /* 從target(點擊)元素向上找currentTarget(監聽)元素, 找到了想委託的元素就觸發事件,沒找到就返回null */ while (target.tagName !== 'LI') { if (target.tagName === 'UL') { target = null break; } target = target.parentNode } if (target) { // console.log('你點擊了ul裏的li') console.log('the content is: ', target.innerHTML); } }); </script>
preventDefault阻止默認行爲
若是調用這個方法,默認事件行爲將再也不觸發。什麼是默認事件呢?例如表單一點擊提交按鈕(submit)刷新頁面、a標籤默認頁面跳轉或是錨點定位等。
a標籤默認頁面跳轉
<a id="test" href="http://www.google.com">連接</a> <script> test.onclick = function(e){ e = e || window.event; e.preventDefault(); } </script>
輸入框最多隻能輸入六個字符
<input type="text" id='tempInp'> <script> tempInp.onkeydown = function(ev) { ev = ev || window.event; let val = this.value.trim() //trim去除字符串首位空格(不兼容) // this.value=this.value.replace(/^ +| +$/g,'') 兼容寫法 let len = val.length if (len >= 6) { this.value = val.substr(0, 6); //阻止默認行爲去除特殊按鍵(DELETE\BACK-SPACE\方向鍵...) let code = ev.which || ev.keyCode; if (!/^(46|8|37|38|39|40)$/.test(code)) { ev.preventDefault() } } } </script>
stopPropagation中止冒泡
event.stopPropagation() 方法阻止事件冒泡到父元素,阻止任何父事件處理程序被執行
<body> <div id="grandfather1"> 爺爺 <div id="parent1"> 父親 <div id="child1">兒子</div> </div> </div> </body> <script> var grandfather1 = document.getElementById('grandfather1'), parent1 = document.getElementById('parent1'), child1 = document.getElementById('child1'); grandfather1.addEventListener('click', function fn1() { console.log('爺爺'); }, false) parent1.addEventListener('click', function fn1() { console.log('爸爸'); event.stopPropagation() }, false) child1.addEventListener('click', function fn1() { console.log('兒子'); }, false)
點擊兒子的時候,打印兒子,爸爸
點擊爸爸的時候,打印爸爸
既能阻止事件向父元素冒泡,也能阻止元素同事件類型的其它監聽器被觸發。而 stopPropagation 只能實現前者的效果
<button id="btn">點我試試</button> <script> const btn = document.querySelector('#btn'); btn.addEventListener('click', event => { console.log('btn click 1'); event.stopImmediatePropagation(); }); btn.addEventListener('click', event => { console.log('btn click 2'); }); document.body.addEventListener('click', () => { console.log('body click'); }); </script>
只打印console.log('btn click 1');
<body> <div id="grandfather1"> 爺爺 <div id="parent1"> 父親 <div id="child1">兒子</div> </div> </div> </body> <script> var grandfather1 = document.getElementById('grandfather1'), parent1 = document.getElementById('parent1'), child1 = document.getElementById('child1'); grandfather1.addEventListener('click', function fn1() { console.log('爺爺'); }, false) parent1.addEventListener('click', function fn1() { console.log('爸爸'); event.stopImmediatePropagation(); }, false) child1.addEventListener('click', function fn1() { console.log('兒子'); }, false) </script>
點擊兒子的時候,打印兒子,爸爸
點擊爸爸的時候,打印爸爸
https://juejin.im/post/5c71e8...
https://github.com/ljianshu/B...
function random() { var arr = [] for (var i = 0; i < 100; i++) { //生成循環100次,生成100個數字。該方法最大的弊端,爲了不有重複的狀況致使數組不足10個元素,因此生成較多的數字 var num = Math.floor(Math.random() * 100) //生成0-100的隨機整數 if (arr.length == 0) { arr.push(num) //數組爲空時直接放入數組 } else { for (var j = 0; j < arr.length; j++) { //循環已存在的數組 if (arr.join(',').indexOf(num) < 0 && arr.length < 10) { //判斷已存在數組中是否已有剛生成的數字,如沒有且數組長度不足10纔將num放入arr arr.push(num) //這樣又會致使生成的大部分數字被arr.length <= 10排除掉了,浪費性能 } } } } return arr }
let set = new Set() while (set.size < 10) { //多少 set.add(Math.round(Math.random() * 10) + 0) //最大值,最小值 } let arr4 = Array.from(set) console.log(arr4)
var arr5 = new Array() while (arr5.length < 10) { var num = Math.round(180 * Math.random()) + 20 var exists = false for (var i = 0, l = arr5.length; i < l; i++) { if (arr5[i] == num) { //判斷是否已經存在 exists = true //存在的話將true傳給exists } } if (!exists) { //如今exist是true,!exists就是false,因此不執行這個if下面代碼。 arr5.push(num) } } console.log(arr5)
var str = "abbcccddddeeee"; function max() { var json = {}; for (var i = 0; i < str.length; i++) { var k = str[i]; //k是全部字符,字符串也跟數組同樣能夠經過中括號下標方法取到每一個子元素 if (json[k]) { json[k]++; //json裏有這個字符時,把這個字符的數量+1, } else { json[k] = 1; //不然把這個字符的數量設爲1 } } console.log(json) var num = 0; var value = null; for (var k in json) { //s、f、g、t、d if (json[k] > num) { num = json[k]; value = k; } } alert("出現最多的字符是:" + value + ',出現次數是:' + num); }; max(str);
http://www.javashuo.com/article/p-asgepdjs-ce.html
https://juejin.im/entry/5a1654e951882554b8373622
ES5只有全局做用域和函數做用域,沒有塊級做用域。
var a = 1; function fn(){ var a = 2; console.log(a) //2 } fn() console.log(a) //1 var a = 1; function fn(){ a = 2; console.log(a) //2 } fn() console.log(a) //2 var a = 1; function fn(a){ var a = 2; //此時a爲傳進來的參數 console.log(a) //2 } fn(a) console.log(a) //1 var a = 1; function fn(a){ a = 2;//此時a爲傳進來的參數 console.log(a) //2 } fn(a) console.log(a) //1
function f1(a) { console.log(a); //10 var a=1; console.log(a); //1 console.log(arguments[0]); //1 } f1(10)
var a = 0; function fn() { console.log(a) // undefined // console.log(b) // defined var a = b = 1; //b至關於全局變量 console.log(b) //1 } fn() console.log(a) //0 console.log(b) //1
var a = 0; function fn(a) { console.log(a) //0 var a = b = 1; console.log(b) //1 } fn(a) console.log(a) //0 console.log(b) //1
https://www.cnblogs.com/mengfangui/archive/2017/12/15/8041317.html