事件模型
JavaScript事件模型主要分为3种:原始事件模型(DOM0事件模型)、DOM2事件模型、IE事件模型。
1.原始事件模型
这是一种被所有浏览器都支持的事件模型,对于原始事件而言,没有事件流,事件一旦发生将马上进行处理,有两种方式可以实现原始事件:
(1)在html代码中直接指定属性值:
(2)在js代码中为 document.getElementsById(“demo”).onclick = doSomeTing()
优点:所有浏览器都兼容
缺点:1)相同事件的监听函数只能绑定一个,后绑定的会覆盖掉前面的,如:a.onclick = func1; a.onclick = func2;将只会执行func2中的内容。2)无法通过事件冒泡进行事件委托。
因为这些缺点,虽然原始事件类型兼容所有浏览器,但仍不推荐使用。
2.DOM2事件模型
此模型是W3C制定的标准模型,现代浏览器(IE6~8除外)都已经遵循这个规范。W3C制定的事件模型中,一次事件的发生包含三个过程:
(1).事件捕获阶段,(2).事件目标阶段,(3).事件冒泡阶段。
在DOM2级中使用addEventListener和removeEventListener来注册和解除事件。addEventListener(“eventType”,“handler”,“true|false”);其中eventType指事件类型,注意不要加‘on’前缀,
事件的传播是可以阻止的:
• 在W3c中,使用stopPropagation()方法,在捕获的过程中stopPropagation();后,后面的冒泡过程就不会发生了。
3.IE事件模型
事件模型只有两步,先执行元素的监听函数,然后事件沿着父节点一直冒泡到document。
1.event. preventDefault()//阻止元素默认的行为,如链接的跳转、表单的提交;
2.event. stopPropagation()//阻止事件冒泡
Promise
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.all了解吗?promise.all可以怎么实现?
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例
(1)只有p1、p2、p3的状态都变成resolved,p的状态才会变成resolved,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
(最初级得版本,没考虑失败,未完待续。)
Promise.all = function(promises) {
let results = [];
return new Promise(function(resolve) {
promises.forEach(function(val) {
// 按顺序执行每一个Promise操作
val.then(function(res) {
results.push(res);
});
});
resolve(results);
});
}
const什么情况可以改变数据?
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于引用类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
基本类型 Number Boolean String undefined null。
地址固定,值就保存在变量指向的那个内存地址。
也就是说,const声明引用类型的对象的话,值是能被改动的。
const声明基本类型的值就不能改动。
const定义引用数据类,不能修改指针指向新的对象 ,但可以修改它指向的数据。
const a={'name':'benben'}
a={'name':'yuanyuan'}
console.log(a) //{'name':'yuanyuan'}
jsonp跨域,如何从a.com网站读取b.com网站的cookie?
传入callback函数作为参数,让另外的域将数据包装至此callback函数
1.原始的跨域方法,利用script标签的src不受同源策略限制。
---------a.com--------------------------
<script type="text/javascript">
var localHandler = function(data){
alert('我是本地函数,可以被跨域的getcookie.php文件调用,远程js带来的数据是:' +JSON.stringify(data) );
};
</script>
<script type="text/javascript" src="http://b.com/getcookie.php?callback=localHandler"></script>
---------b.com--------------------------
getcookie.php
<?php
header("Access-Control-Allow-Origin:*");
header("Access-Control-Allow-Methods:POST,GET");
$callback = $_GET['callback'];
$cookie = json_encode($_COOKIE);
echo $callback."(".$cookie.")";
?>
2.npm install jsonp ,安装jsonp插件
getCookie(){
let url = "https://b.com/getcookie.php"
//jsonp方法会自动添加callback
jsonp(url,{},(err,data)=>{
//url地址 //{}参数 //(err,data)错误信息,获取的数据
console.log(data)
})
js中的事件委托(事件代理)了解吗?
事件委托是利用事件的冒泡原理来实现的,当我们给最外面的元素填加点击事件,那么里面的子元素发生事件的时候,都会冒泡到最外层的父元素上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。可以通过event.target来找到触发事件的具体子元素。
<div class="main">
<ul id="p">
<li>我是肖杰</li>
<li>我是呱呱</li>
</ul>
</div>
document.getElementById('p').onclick=function(){
alert(event.target.innerHTML)
}
异步加载js的方式
按照传统的做法,所以的
对象深拷贝的方式
浅拷贝:只拷贝对象的内存地址。
1.Object.assign()浅拷贝;{…}如果拷贝的对象里面还包含子对象,那子对象也是浅拷贝
var obj={
'name':'肖卷卷',
'hobby':{'dance':'locking'}
}
var b =Object.assign(obj)
b.hobby.dance='poping'
b.name='肖美丽'
console.log(obj)
2.JSON.parse(JSON.stringify(obj))深拷贝
var obj={
'name':'肖卷卷',
'hobby':{'dance':'locking'}
}
var x =JSON.stringify(obj)
var res = JSON.parse(x)
res.hobby.dance='jazz'
console.log(obj)
3.Array.from()浅拷贝,里面包含的对象,拷贝对象的内存地址。
var arr = [1,2,{'name':'呱呱','hobby':{'play':'eat'} }]
var arr1 = Array.from(arr)
arr1[0]=100
arr1[2].hobby.play='sleep'
console.log(arr)
什么是面向对象
与面向对象相对的是面向过程
这两个有什么区别呢?
举个例子,把大象放进冰箱需要几步?
面向过程:程序员们会逐个打开冰箱的门,每增多一个冰箱,就去打开一次门。
面向对象:图纸上设计了一个冰箱,这个冰箱会自己把门打开。只要是按照这个图纸生产出来的冰箱,就可以自己把门打开,自己把大象装进去,自己把门关上。人们不需要了解这一过程是如何实现的,只要在需要把大象装进冰箱的时候,跟冰箱说一句:“嘿,你把大象装进去吧。”
像这样子,在解决问题的过程中,分析出每个参与解决问题的对象(冰箱),并确定这些对象的行为(开门,装大象,关门),最终由这些对象解决问题的编程思想,被称为面向对象的编程思想(Object Oriented Programming,简称OOP)。在面向对象编程中,这张图纸就被称为类,而按照这张图纸生产出来的一台台冰箱,则被称为类的实例或者对象,这一生产过程,就被称为类的实例化。
也许有人会问:我要这图纸有何用?我写个函数,一样可以实现这个功能,只要在需要开门的时候调用这个函数就行了,又何需考虑自动开门的冰箱怎么设计?
这个想法固然好,但问题在于,每当冰箱换个型号,我们就要因为其中的微小变动而重写一遍这个函数。
面向对象的三大特性:封装、继承和多态。
封装:对象为它内部的数据提供了不同级别的保护,确定了哪些数据只能由谁访问,通过这样的方式,我们可以有效地阻止程序运行过程中的某些意外错误地修改了无关的数据。
继承:赋予新创建的类一种能力:它可以使用现有类的全部功能,而不需要为了扩展现有类的功能而重写代码。
多态:指允许不同类的对象对同一消息作出响应。比如同样的加法,把两个时间加在一起和把两个整数加在一起肯定完全不同。继承为了 —— 代码重用。而多态则是为了—— 接口重用
JS设计模式
工厂模式
构造器模式
需要用到new操作符,会执行如下操作:
- 在内存中创建一个新对象
- 令新对象的__proto__属性,等于构造函数的prototype属性
- 构造函数内部的this指向这个新对象
- 执行构造函数内部的代码,也就是给新对象添加属性
- 返回该对象
function Person() {
var this = {
__proto__: Person.prototype
}
this.name = name
this.age = age
return this
}
var person = new Person()
相比于工厂模式,自定义构造函数可以确保实例被表示为特定的类型。
原型模式
每个函数都有prototype属性,我们可以把定义在构造器中的属性和方法定义原型prototype上面,这样就可以被实例所共享。
模块模式
什么是模块化:现代的js开发会写大量代码和广泛使用第三方库。模块模式就是将代码查分成独立的块,然后再把这些块连接起来。思想就是:把代码逻辑分块,各自封装,相互独立。指定一个模块作为入口,每个模块引入所需依赖,自行决定对外暴露什么。使用ES6模块逐渐成为主流,但是ES6模块目前无法直接运行,需要转换成ES5.
安全方面的问题
跨站脚本攻击(xss)
利用虚假表单,骗取用户信息。
csrf ,网页注入
Ajax是怎么实现的?
- 创建XMLHttpRequest对象。
- 设置请求方式。
- 调用回调函数。
- 发送请求。
//1:创建Ajax对象
var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// 兼容IE6及以下版本
//2:配置 Ajax请求地址
xhr.open('get','index.xml',true);
//3:发送请求
xhr.send(null); // 严谨写法
//4:监听请求,接受响应
xhr.onreadysatechange=function(){
if(xhr.readySate==4&&xhr.status==200 || xhr.status==304 )
console.log(xhr.responsetXML)
}
手写apply、call、bind函数
call方法
Function.prototype.myCall = function(thisArg, ...args) {
const fn = Symbol('fn') // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
thisArg = thisArg || window // 若没有传入this, 默认绑定window对象
thisArg[fn] = this // this指向调用call的对象,即我们要改变this指向的函数
const result = thisArg[fn](...args) // 执行当前函数
delete thisArg[fn] // 删除我们声明的fn属性
return result // 返回函数执行结果
}
//测试
foo.myCall(obj)
apply方法
Function.prototype.myApply = function(thisArg, args) {
const fn = Symbol('fn') // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
thisArg = thisArg || window // 若没有传入this, 默认绑定window对象
thisArg[fn] = this // this指向调用call的对象,即我们要改变this指向的函数
const result = thisArg[fn](...args) // 执行当前函数
delete thisArg[fn] // 删除我们声明的fn属性
return result // 返回函数执行结果
}
//测试
foo.myApply(obj, [])
bind方法
Function.prototype.myBind = function (thisArg, ...args) {
var self = this
// new优先级
var fbound = function () {
self.apply(this instanceof self ? this : thisArg, args.concat(Array.prototype.slice.call(arguments)))
}
// 继承原型上的属性和方法
fbound.prototype = Object.create(self.prototype);
return fbound;
}
//测试
const obj = { name: '写代码像蔡徐抻' }
function foo() {
console.log(this.name)
console.log(arguments)
}
foo.myBind(obj, 'a', 'b', 'c')()
以下是不支持new方法的
Function.prototype.mybind = function() {
console.log([...arguments])
var thatFunc = this, thatArg = arguments[0];
var args = Array.prototype.slice.call(arguments, 1);
if (typeof thatFunc !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - ' +
'what is trying to be bound is not callable');
}
return function(){
console.log([...arguments]) //[4,5,6]
var funcArgs = args.concat([...arguments])
return thatFunc.apply(thatArg, funcArgs);
};
};
var name = 'jay'
var abc=function(){
console.log(this.name) //rose
}
var obj={'name':'rose'}
abc.mybind(obj,1,2,3)(4,5,6)