赞
踩
分为捕获型、冒泡型,addEventListener的第三个参数,为true是捕获型,为false是冒泡型(即默认不写是冒泡型)
常用的事件:click、mouseover(支持冒泡)、mouseout(支持冒泡)、mouseenter(不支持冒泡)、mouseleave(不支持冒泡)、blur(失焦支持冒泡)、focus(聚焦支持冒泡)、keyup(释放键盘)、keydown(按住键盘)、resize。
详见:JavaScript事件类型 - Wayne-Zhu - 博客园
event对象:target(事件触发的dom对象)、currentTarget(监听事件的dom对象)、stopPropagation(阻止冒泡)、preventDefault(阻止默认事件)
兼容写法:
event.preventDefault ? event.preventDefault() : (event.returnValue = false);
event.stopPropagation ? event.stopPropagation() : (event.cancelBubble = true);
event.stopPropagation其实是阻止事件的传播,并不是仅仅只是阻止冒泡,他连捕获事件也会阻止的
event.stopImmediatePropagation是在stopPropagation的基础上,把当前节点的同类事件也给阻止
document.getElementById('app').addEventListener('click', (e) => {
// 如果是e.stopPropagation()的话,下面会执行
e.stopImmediatePropagation()
console.log(e.currentTarget.tagName)
})
document.getElementById('app').addEventListener('click', (e) => {
// 这里不会执行
console.log(e.currentTarget.tagName)
})
document.getElementById('app').addEventListener('custom', () => {});
document.getElementById('app').dispatchEvent('custom', 'aaa')
每一个function方法都有一个prototype,每一个由方法new出来的对象都有一个_proto_,然后function的prototype和对象的_proto_是相等的,可以利用这个特性实现js的继承
A.prototype = new B();
Function和Object的关系
Function和Object都是构造函数,他们都是Function的实例对象,即Function.__proto__ === Function.prototoye
Object.__proto__ === Function.prototype
Object instanceof Function === true Function instanceof Object === true
封装(函数封装)、继承(原型链继承)、多态(arguments参数多态)
闭包、遗忘的定时器、dom引用
- function test(a, b, c) {
- console.log(a, b, c);
- }
- Function.prototype.bindNew = function() {
- var self = this
- var params = arguments
- var context = params[0]
- var args = Array.prototype.slice.call(params, 1)
- return (...rest) => {
- self.apply(context, args.concat(rest))
- }
- }
-
- test.bindNew(window, 1, 2)(3);
在外部获取函数体内部变量的值,就叫闭包
- function test() {
- var num = 1;
- return function() {
- return num;
- }
- }
- var data = test()();
- console.log(data);
-
-
- //输出5个5
- for (var i = 0; i < 5;i++) {
- setTimeout(() => {
- console.log(i)
- }, 100)
- }
-
- 通过闭包可以解决问题,输出01234
- for (var i = 0; i < 5;i++) {
- (function(a){
- setTimeout(() => {
- console.log(a)
- }, 100)
- })(i)
- }
-
- 通过let也可以解决,输出01234
- for (let i = 0; i < 5;i++) {
- setTimeout(() => {
- console.log(i)
- }, 100)
- }
-
- setTimeout的第三个参数,可以给第一个参数的方法传递参数,也可以做到输出01234
- for (var i = 0; i < 5;i++) {
- setTimeout((a) => {
- console.log(a)
- }, 100, i)
- }
- 伪类:
- :link
- :visited
- :hover
- :active
- :first-child
- :first-of-type
- :last-child
- :last-of-type
-
- 伪元素:
- ::before
- ::after
-
- .xxx:first-child和.xxx:first-of-type 的区别?
- (https://www.jianshu.com/p/b6981849ab3b)
-
- :first-child匹配的是某个父元素的第一个子节点的class是否为xxx,如果不是,就没有
- :first-of-type匹配的是某个父元素的第一个class为xxx的子节点(父元素的第二个子节点为xxx也能匹配到)
-
- 为什么要清除浮动?
-
- 因为子元素是浮动,就脱离了文档流,父元素的高度就会出现异常,如果这个时候给父元素设置背景色或者边框什么的样式的话,就和想象中不一致,这个时候就必须要用清除浮动来撑开父元素的高度
-
-
- 清除浮动:
- <!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>
- </head>
- <style>
- html,
- body {
- width: 100%;
- height: 100%;
- position: relative;
- }
-
- .clearfix {
- zoom: 1;
- }
-
- .clearfix:after {
- display: block;
- clear: both;
- content: "";
- }
-
- .left {
- width: 100px;
- height: 100px;
- background-color: red;
- float: left;
- }
-
- .right {
- width: 100px;
- height: 100px;
- background-color: black;
- float: right;
- }
- </style>
-
- <body>
- <div class="clearfix">
- <div class="left"></div>
- <div class="right"></div>
- </div>
- </body>
-
- </html>
-
- 值引用(为了解决下面这个问题,就出现了深浅拷贝):
- var obj1 = { a: 1 };
- var obj2 = obj1 ;
- obj2.b = 2;
- console.log(obj1);
- console.log(obj2);
-
- 深拷贝:
- lodash的cloneDeep
-
- for in 循环可以循环一个对象的原型链的属性,Object.keys就拿不到原型链里的属性
- for in 通过hasOwnProperty可以判断是否是自己的属性,而不是原型链的属性
-
-
- 浅拷贝:
- var obj1 = { a: 1 };
- var obj2 = $.extend({}, obj1); // 或者用var obj2 = Object.assign({}, obj1)
- obj2.b = 2;
- console.log(obj1);
- console.log(obj2);
-
- 浅拷贝有一个问题就是,多层次会有问题,多层次直接覆盖对象,不会进行合并
- var a = {a:1, b: {c:1, d:2}}
- var b = {a:2, b: {c:2, e:3}}
- var c = Object.assign(a, b) // 或者var c = $.extend(a, b)
- console.log(a) // 输出 {a:2, b: {c:2, e:3}}
- console.log(c) // 输出 {a:2, b: {c:2, e:3}}
- console.log(a === c) // 输出 true
- a.b.c = 4
- console.log(a) // 输出 {a:2, b: {c:4, e:3}}
- console.log(c) // 输出 {a:2, b: {c:4, e:3}}
-
- 这个时候的浅拷贝,实际b还是个指针形式,a改变了还是c会被改变
-
- 用lodash的merge方法,可以实现想要的merge
- var a = {a:1, b: {c:1, d:2}}
- var b = {a:2, b: {c:2, e:3}}
- var c = _.merge(a, b)
- console.log(c) // {a:2, b: {c:2, d:2, e:3}}
- console.log(a) // {a:2, b: {c:2, d:2, e:3}}
-
- 深拷贝的循环引用使用 weakMap 做缓存,遇到value在weakMap中,就不做深拷贝,直接用 weakMap 中的值
margin、border、padding、content
- box-sizing: border-box; // IE盒子 width=content+padding+border
- box-sizing: content-box; // W3C标准盒子 width=content
- 1、DNS解析域名获取到IP
- 2、客户端和服务端建立连接(三次握手)
- 3、客户端向服务端发起请求
- 4、服务端根据不同请求返回不同的状态码和对应数据给客户端(200,304缓存,404,500等)
- 5、客户端拿到数据之后开始解析,根据HTML生成DOM树,CSS文件生成CSSOM树,然后由这2个树生成一个渲染树对页面进行渲染
-
-
- 图片资源如果是display:none,也会发网络请求
- 1、浏览器查看缓存是否有该域名的访问记录,有的话返回IP
- 2、本机查询hosts文件中是否有该域名的配置,有的话返回IP
- 3、路由器查询缓存中是否有该域名的访问记录,有的话返回IP
- 4、再到ISP(互联网服务提供商)DNS缓存中查询该域名的IP,有的话返回IP
- 5、到全球仅有的13个根域名服务器群中查询,有的话返回IP
- 6、顶级域名服务器中查询,有的话返回IP
递归解析:一直透传
迭代解析:先查询浏览器缓存是否有记录,如果没有,就返回,然后再到hosts文件中查询是否有记录,没有就再返回。。
- 1、form表单可以跨域,因为form表单提交之后,是收不到响应的,浏览器认为这样是安全的。
- 2、浏览器的同源策略,限制的是不能从其他域拿到数据,但是它并不阻止你向其他域发请求
- 3、form表单提交会带cookie,比如<form action="https://www.baidu.com/"></form>就会带上
- baidu.com的cookie,但是baidu.com如果限制了samesite的cookie,是不会带的,form表单提交是可以做csrf攻击的
- 4、ajax和fetch跨域的时候默认是不会携带cookie的,如果要携带的话,fetch可以增加credentials参数(same-origin默认值、include携带、omit不携带),ajax可以增加withCredentials参数(true携带、false不携带),但是samesite的cookie一样是不会带的。另外在增加了credentials和withCredentials参数之后,后端设置Access-Control-Allow-Origin不能为*,只能为指定的域名,且后端必须设置 Access-Control-Allow-Credentials: true
- AMD依赖前置,CMD依赖就近。AMD依赖加载完再往下运行,CMD由上往下,边加载依赖,边运行
-
- 1、CommonJS是被加载的时候运行,ESModule是编译的时候运行
- 2、CommonJS输出的是值的浅拷贝,ESModule输出的是引用
- 3、CommonJS第一次require模块的时候会运行整个文件并输出,后面再次require就会从内存中取值
移动端视口
设计稿是750px的,以iphone6为准,iphone6设备是750px宽,它上面的html字体大小是75px,然后设计稿中为375px宽的按钮,就是5rem,公式是rem = (设计稿上的像素 / 75) * 1rem;就是1rem=75px
- H5和客户端通信 => JSBridge
-
- 1、客户端调用H5,客户端可以通过代码调用webview里面的window下的方法,将需要传递的数据通过参数传给H5
-
- 2、H5调用客户端,客户端会在webview中注册一个方法放在window下,然后H5调用window下的这个方法,就可以将数据传递给客户端
- @media only screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) {
- .container{
- padding: constant(safe-area-inset-top) 0 constant(safe-area-inset-bottom) 0;
- padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom) 0;
- }
- .prepare{
- padding-top: pxToRem(75px);
- padding-bottom: pxToRem(75px);
-
- .btn{
- margin-top: pxToRem(44px);
- }
- }
- .balance-con{
- margin-bottom: constant(safe-area-inset-bottom);
- margin-bottom: env(safe-area-inset-bottom);
- }
- .invite-code{
- bottom: 0 0 constant(safe-area-inset-bottom) 0;
- bottom: 0 0 env(safe-area-inset-bottom) 0;
- }
- .service{
- bottom: constant(safe-area-inset-bottom);
- }
- }
- 点击穿透:
- touch事件分为touchstart、touchend,touch和click中间相隔300ms
-
- 为什么有300ms延迟:是因为移动端要有双击缩放的需求,就用了300ms的延迟
-
- 遮盖的元素绑定touch事件,
- 被遮盖的元素绑定click事件,就会有点击穿透,被遮盖的元素也会触发click事件
-
- 解决方法:
- 1、在遮盖元素延时350ms再隐藏
- 2、遮盖元素绑定touchend事件,在事件中让被遮盖元素增加一个样式pointer-events:none;
-
- 去除300ms的途径:
- 1、meta设置禁用缩放
- 2、FastClick的实现原理是在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后的click事件阻止掉
-
-
- 滚动穿透:
- 弹出框是fixed,然后再弹出啊框滑动的时候,会让页面滑动
-
- 解决方法:
-
- 如果弹出框内部不需要滑动:
- $('.mask').on('touchmove', (e)=> {
- e.preventDefault();
- })
- 阻止默认事件,能阻止页面滑动
-
- 如果弹出框内部需要滑动
- 在弹出框出现的时候,设置$('html,body').css('overflow', 'hidden');
- 在弹出框消失的时候,设置$('html,body').css('overflow', 'auto');
- var promise = new Promise((resolve, reject) => {
- if (true) {
- setTimeout(() => {
- resolve(1);
- });
- } else {
- setTimeout(() => {
- reject(0);
- });
- }
- });
- promise.then((res) => {
- console.log(res);
- });
- cookie的作用:
- 1.可以在客户端上保存用户数据,起到简单的缓存和用户身份识别等作用。
- 2.保存用户的登陆状态,用户进行登陆,成功登陆后,服务器生成特定的cookie返回给客户端,客户端下次访问该域名下的任何页面,将该cookie的信息发送给服务器,服务器经过检验,来判断用户是否登陆。
- 3.记录用户的行为。
-
- cookie弊端:
- 1.增加流量消耗,每次请求都需要带上cookie信息。
- 2.安全性隐患,cookie使用明文传输。如果cookie被人拦截了,那人就可以取得所有的session信息。
- 3.Cookie数量和长度的限制。每个domain最多只能有20条cookie,每个cookie长度不能超过4KB,否则会被截掉
- 1、箭头函数不能用new
- 2、箭头函数this指向是上下文,构造函数this指向生成的对象,其他的函数this指向是调用函数的对象
- 3、箭头函数没有prototype
- 4、箭头函数没有arguments,取而代之的是rest参数
- const func = (...rest) => {
- console.log(rest); // [1,2,3]
- }
- func(1,2,3);
- var obj = new AAA();
- 等同于
- var obj = {};
- obj._proto_ = AAA.prototype;
- AAA.call(obj);
-
- (1) 创建一个新对象;
- (2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
- (3) 执行构造函数中的代码(为这个新对象添加属性) ;
- (4) 返回新对象。
-
- 实现new的功能
- function SelfNew() {
- var params = Array.prototype.slice.call(arguments, 1)
- var func = arguments[0]
-
- var obj = {}
- obj.__proto__ = func.prototype
- var result = func.apply(obj, params)
- if (typeof result === 'object' && result !== null) {
- return result
- } else {
- return obj
- }
- }
-
- function A(a, b) {
- this.a = a
- this.b = b
- return 1
- }
-
- var a = SelfNew(A, 1, 2)
-
- console.log(a)
-
- console.log(new A(1,2))
-
-
- 构造函数如果return了基础数据类型,就对new出来的对象无影响,
- 如果return了复杂数据类型,就是return的那个对象
-
- function A() {
- this.name = 'name'
- this.sex = 'male'
- return 1
- }
-
- function B() {
- this.name = 'name'
- this.sex = 'male'
- return {}
- }
-
- var a = new A()
- var b = new B()
- console.log(a) // {name: 'name', sex: 'male}
- console.log(b) // {}
-
- new.target可以判断一个函数是否是以构造函数的形式被调用的
-
- function Person(name) {
- if (new.target !== undefined) {
- this.name = name;
- } else {
- throw new Error('必须使用 new 命令生成实例');
- }
- }
-
- // 另一种写法
- function Person(name) {
- if (new.target === Person) {
- this.name = name;
- } else {
- throw new Error('必须使用 new 命令生成实例');
- }
- }
个人理解:从里到外的作用域形成的链
分为全局作用域和函数作用域,然后函数内部可以通过作用域链去访问到他的父函数乃至全局作用域下的一些变量。
- var a = 1;
- function A() {
- var b = 2;
- function B() {
- var c = 3;
- console.log(a); //1
- console.log(b); //2
- }
- }
-
- 函数B可以通过作用域链,访问到父函数A环境下里的变量b,也可以访问到全局环境的变量a
首先先来了解什么是作用域,即变量(变量作用域又称上下文)和函数生效(能被访问)的区域或集合
换句话说,作用域决定了代码区块中变量和其他资源的可见性 我们一般将作用域分成:
全局作用域:任何不在函数中或是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问
函数作用域:函数作用域也叫局部作用域,如果一个变量是在函数内部声明的它就在一个函数作用域下面。这些变量只能在函数内部访问,不能在函数以外去访问
块级作用域:ES6引入了let
和const
关键字,和var
关键字不同,在大括号中使用let
和const
声明的变量存在于块级作用域中。在大括号之外不能访问这些变量
什么是作用域链:当在Javascript
中使用一个变量的时候,首先Javascript
引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域
如果在全局作用域里仍然找不到该变量,它就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错
- 1、var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
- 2、let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
- 3、const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
-
- 为什么要有let和块级作用域??
-
- 函数作用域会有这样的现象,函数内部的同名变量会覆盖外层的变量,块级作用域就不会有这样的问题,let创建的变量会出现块级作用域,let声明的变量不会出现变量提升,不能重名,因为会出现暂时性死区
- == 会做类型转换
-
- === 不会做类型转换
-
- Object.is会在===的基础上,特殊处理了NaN,0,-0的情况
-
- NaN === NaN // false
- 0 === -0 // true
- Object.is(NaN,NaN) // true
- Object.is(0,-0) // false
-
-
- 为什么typeof可以检测类型?
- js在底层存储变量的时候会在变量的机器码的低位1-3位存储其类型信息(000:对象,010:浮点数,100:字符串,110:布尔,1:整数),但是null所有机器码均为0,直接被当做了对象来看待
null表示没有对象,即此处不应该有值。undefined表示缺少值,即此处应该有值,但没有定义
1、图片预加载,请求懒加载
2、工程化压缩合并js、css文件,减少文件请求数量
3、用多个域名存放文件
4、本地缓存localstorage
5、css动画替换js动画,H5播放器替换flash播放器
6、webpack图片压缩插件,路由拆分代码,按需加载
请求优化 图片延迟加载 ajax局部加载数据 预加载 资源优化 资源压缩(uglify-js,clean-css) 资源合并 自动化构建(gulp) 图片合并csssprite iconfont 引用优化 单独域名存放资源 缓存 Cache-Control缓存策略 离线存储 本地存储localStorage 其它的 css3替换js动画 替换flash 结语
- xss是跨站脚本,在输入的地方写html或js,然后服务端接受到了,在其他客户端会收到这串脚本,就会执行
- Html
-
- encode防止xss,js-xss库防止xss
- csrf是跨站请求攻击,恶意用户通过某种途径拿到了正常用户的cookie,然后通过cookie去模拟正常用户去发送一些恶意请求
- 验证 HTTP Referer 字段,判断来源
- Token防止csrf,尽量不要在cookie中暴露用户隐私信息
- same-site字段防止第三方网站
-
- 举例:
- 比如你登录了一个银行网站A,银行网站A里面有个请求是更新你的账户数据的,然后这个时候你进入了一个危险网站B,这个危险网站B里面会调用银行网站A里面的这个更新数据的接口,那么你就会发出一个银行网站A的更新数据的请求,并且会带上银行网站A的cookie,这个时候就是csrf
-
- 用token,就可以在调用这个接口的时候,让你带不上这个token,带不上token服务端就不会进行更新操作了
-
- 问题:如果token被盗用了怎么办?
-
- token 不是用来做用户信息加密的,而是和 session 一样用来做身份鉴别的,至于信道安全性还是需要用 https 保证
- JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。
- JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。
- slice(): 第一个参数是开始位置(第一个从0开始),第二个参数(可省)是结束位置,
- 如果没有第二个参数,默认就是最后一个位置,但不包括结束位置的项,为负数就加上数组长度进行转换。
- 源数组不会改变。
- var arr = [1,3,5,7,9,11];
- var arrCopy = arr.slice(1);
- var arrCopy2 = arr.slice(1,4);
- var arrCopy3 = arr.slice(1,-2);
- var arrCopy4 = arr.slice(-4,-1);
- console.log(arr); //[1, 3, 5, 7, 9, 11](原数组没变)
- console.log(arrCopy); //[3, 5, 7, 9, 11]
- console.log(arrCopy2); //[3, 5, 7]
- console.log(arrCopy3); //[3, 5, 7]
- console.log(arrCopy4); //[5, 7, 9]
-
- splice():
- 2个参数代表删除,第一个参数是开始位置(第一个从0开始),第二个参数是要删除的个数(传0代表不删除),源数组会改变。
- 3个参数(或者更多参数)代表删除后再插入,第一个参数是开始位置(第一个从0开始),第二个参数是要删除的个数(传0代表不删除),第三个参数为插入的值。
-
- var arr = [1,3,5,7,9,11];
- var arrRemoved = arr.splice(0,2);
- console.log(arr); //[5, 7, 9, 11]
- console.log(arrRemoved); //[1, 3]
- var arrRemoved2 = arr.splice(2,0,4,6);
- console.log(arr); // [5, 7, 4, 6, 9, 11]
- console.log(arrRemoved2); // []
- var arrRemoved3 = arr.splice(1,1,2,4);
- console.log(arr); // [5, 2, 4, 4, 6, 9, 11]
- console.log(arrRemoved3); //[7]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。