基本类型(值类型)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