JavaScript知识点整理(2)

  1. global window
    global 是 javascript 运行时所在宿主环境提供的全局对象,是一个 Object。目前来说最常见的宿主环境是浏览器和 nodejs,在浏览器里,非严格模式下(印象中必须是这个),global等于window
  2. 本地对象 内置对象 宿主对象
    本地对象,就是那些官方定义好了的对象,“本地对象”包含 Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError。本地对象需要实例化(new)。内置对象是本地对象的一种,其只包含Global对象和Math对象,内置对象不需要开发者进行实例化。而宿主对象则是那些官方未定义,宿主环境提供的对象如DOM和BOM。
  3. JS单线程和异步机制
    在浏览器的一个页面中,该页面的JS程序只有一个线程,故曰单线程。因为是单线程,所以程序的执行顺序就是从上到下依次执行,同一时间内只能有一段代码被执行。虽然JavaScript是单线程的,可是浏览器内部不是单线程的。你的一些I/O操作、定时器的计时和事件监听(click, keydown…)等都是由浏览器提供的其他线程来完成的。

    浏览器的并发模型
    栈存储的是同步任务,所谓同步的任务就是那些能立即执行、不耗时的任务,如变量和函数的初始化、事件的绑定等等那些不需要回调函数的操作都可归为这一类。堆用来存储声明的变量、对象。队列就是任务队列,一旦某个异步任务有了响应就会被推入队列中。如用户的点击事件、浏览器收到服务的响应和setTimeout插入的事件。每个异步任务都和一个回调函数相关联。

一个js程序的单线程用来执行栈中的同步任务,当所有同步任务执行完毕后,栈被清空,然后读取任务队列中的一个待处理任务,并把相关回调函数压入栈中,单线程开始执行新的同步任务,执行完毕。

单线程从任务队列中读取任务是不断循环的,每次栈被清空后,都会在任务队列中读取新的任务,如果没有新的任务,就会等待,直到有新的任务,这就叫任务循环。因为每个任务都由一个事件所触发,所以也叫事件循环。

拿ajax来说,当页面的单线程执行xhr.send()之后,对于页面来说发送任务已经完成了。怎么发送,那是浏览器的事,和单线程无关;什么时候响应,这事说不准。为了及时地得到响应的内容,在单线程中注册相应的事件就好xhr.onreadystatechange = fn() {…}。注册之后,浏览器会在内部的其他线程中自动地帮我们监听该事件。直到该事件被触发,浏览器会在任务队列中添加一个任务等待该单线程执行。setTimeout的作用是在间隔一定的时间后,将回调函数插入任务队列中,等栈中的同步任务都执行完毕后,再执行。因为栈中的同步任务也会耗时,所以间隔的时间一般会大于等于指定的时间。setTimeout(fn, 0)的意思是,将回调函数fn立刻插入任务队列,等待执行,而不是立即执行。
4. WebSocket
WebSocket是不同于HTTP的另一种协议,都是基于TCP协议, HTTP1.1相比HTTP1.0虽然多了keep alive的长连接,但是只是减少了三次握手和四次挥手的成本,http是无状态协议,每次request仍然要重复发送全部header的内容,并没有做到真正的长连接,而且http只能是客户端主动发起链接,服务器端是没法主动联系客户端的,基于这些缺陷,WebSocket出现了,它也是在TCP之上,端口是80和443,有很好的兼容性,握手阶段使用的是http协议,然后再与服务器沟通升级为Websocket协议,WebSocket仍然是可加密的,即在TCP协议之上使用TLS协议,WebSocket的标识符是ws,加密的WebSocket的标识符是wss

//客户端WebSocket示例
var ws = new WebSocket("wss://echo.websocket.org");//新建WebSocket实例,客户端与服务器进行连接

ws.onopen = function(evt) { //指定连接成功后的回调函数,如果要指定多个回调函数,可以使用addEventListener方法。
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");//向服务器发送数据
};

ws.onmessage = function(evt) {//指定收到服务器数据后的回调函数
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {//指定连接关闭后的回调函数
  console.log("Connection closed.");
};      

WebSocket握手

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket  //告诉服务器我发起的是WebSocket协议
Connection: Upgrade  //告诉服务器我发起的是WebSocket协议
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== //浏览器随机生成的Base64 encode
Sec-WebSocket-Protocol: chat, superchat //用户定义的字符串,用来区分同URL下,不同的服务所需要的协议
Sec-WebSocket-Version: 13//告诉服务器所使用的Websocket版本
Origin: http://example.com

然后服务器会返回下列东西,表示已经接受到请求, 成功建立Websocket

HTTP/1.1 101 Switching Protocols
Upgrade: websocket //告诉客户端即将升级的是Websocket协议
Connection: Upgrade //告诉客户端即将升级的是Websocket协议
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= //经过服务器确认,并且加密过后的 Sec-WebSocket-Key
Sec-WebSocket-Protocol: chat

这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议.

如果客户端不支持WebSocket可以用ajax轮询和http long poll达到类似的效果,ajax轮询是客户端不断向服务器发送请求,long poll则是客户端发送请求后,服务端一直等待,直到有数据后再回应response

  1. session
    由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建特定的Session,用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。

思考一下服务端如何识别特定的客户?这个时候Cookie就登场了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。

Cookie其实还可以用在一些方便用户的场景下,设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,能够方便一下用户。

所以,总结一下:
Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中,默认保存在文件中;用户验证这种场合一般会用 session
Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。
6. 闭包应用-单例模式
单例就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

var SingletonTester = (function () {

    //参数:传递给单例的一个参数集合
    function Singleton(args) {

        //设置args变量为接收的参数或者为空(如果没有提供的话)
        var args = args || {};
        //设置name参数
        this.name = 'SingletonTester';
        //设置pointX的值
        this.pointX = args.pointX || 6; //从接收的参数里获取,或者设置为默认值
        //设置pointY的值
        this.pointY = args.pointY || 10;

    }

    //实例容器
    var instance;

    var _static = {
        name: 'SingletonTester',

        //获取实例的方法
        //返回Singleton的实例
        getInstance: function (args) {
            if (instance === undefined) {
                instance = new Singleton(args);
            }
            return instance;
        }
    };
    return _static;
})();

var singletonTest = SingletonTester.getInstance({ pointX: 5 });
console.log(singletonTest.pointX); // 输出 5 

另一种实现方法,静态类,无法在初始化的时候添加信息

function Universe() {

    // 判断是否存在实例
    if (typeof Universe.instance === 'object') {
        return Universe.instance;
    }

    // 其它内容
    this.start_time = 0;
    this.bang = "Big";

    // 静态变量,缓存
    Universe.instance = this;

    // 隐式返回this
}

// 测试
var uni = new Universe();
var uni2 = new Universe();
console.log(uni === uni2); // true

单例模式应用-创建遮罩层

var createMask = function(){
  var mask;
  return function(){
       return mask || ( mask = document.body.appendChild( document.createElement('div') ) )
  }
}()

更通用的单例包装器, 可传入实现不同功能的函数

var singleton = function( fn ){
    var result;
    return function(){
        return result || ( result = fn .apply( this, arguments ) );
    }
}

var createMask = singleton( function(){

return document.body.appendChild( document.createElement('div') );

 })
  1. JavaScript 类型转换
    ==, 等于,两边值类型不同的时候,要先进行类型转换,再比较。
    ===,恒等,不做类型转换,类型不同的一定不等。
    先说 ===,这个比较简单。下面的规则用来判断两个值是否===相等:
    1、如果类型不同,就[不相等]
    2、如果两个都是数值,并且是同一个值,那么[相等];例外的是,如果其中至少一个是NaN,那么[不相等]。(判断一个值是否是NaN,只能用isNaN()来判断,NaN!=NaN)
    3、如果两个都是字符串,每个位置的字符都一样,那么[相等];否则[不相等]。
    4、如果两个值都是true,或者都是false,那么[相等]。
    5、如果两个值都引用同一个对象或函数,那么[相等];否则[不相等]。
    6、如果两个值都是null,或者都是undefined,那么[相等]。
    再说 ==,根据以下规则:
    1、如果两个值类型相同,进行 === 比较。
    2、如果两个值类型不同,他们可能相等。根据下面规则进行类型转换再比较:
    a、如果一个是null、一个是undefined,那么[相等]。不能将null和undefined转换成任何值
    b、如果一个是字符串,一个是数值,把字符串转换成数值再进行比较。
    c、如果任一值是 true,把它转换成 1 再比较;如果任一值是 false,把它转换成 0 再比较。
    d、如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的valueOf方法。 例外的是Date,Date利用的是toString转换。
    e、任何其他组合,都[不相等]。

JavaScript原始类型(primitive value):String Number Boolean, 在JavaScript进行对比或者各种运算的时候会把对象转换成这些类型,从而进行后续的操作.

object.toString() 返回 “[object type]”,其中type是对象类型(Object Math Date String), object.valueOf()返回对象自身
array.toString() 返回数组内元素组成的字符串,数组中的元素之间用逗号分隔, 同array.valueOf()

在某个操作或者运算需要字符串的时候,往往会触发Object的String转换
- 如果toString方法存在并且返回“原始类型”,返回toString的结果。
- 如果toString方法不存在或者返回的不是“原始类型”,调用valueOf方法,如果valueOf方法存在,并且返回“原始类型”数据,返回valueOf的结果。
- 其他情况,抛出错误。

当需要使用Number时,( 如Math.sin() )等,解释器会尝试将对象转换成Number对象。通常有如下的情况会触发Number转换
- 方法参数需要Number的时候,如Math.sin(obj)等
- 对比的时候,如 obj == ‘abc’
- 运算的时候,如 obj + 123
转换规则如下:
- 如果valueOf存在,且返回“原始类型”数据,返回valueOf的结果。
- 如果toString存在,且返回“原始类型”数据,返回toString的结果。
- 报错。

在进行布尔比较的时候,比如 if(obj) , while(obj)等等,会进行布尔转换,布尔转换遵循如下规则:
- true/false -> true/false
- undefined,null -> false
- Number -> 0,NaN 对应 false, 其他的对应 true
- String -> “”对应false,其他对应true(’0’对应的是true)
- Object -> true

[] == ![]  //true

// 首先第一步右边的是逻辑判断![],说以先转成boolean
// [] == !true
// [] == false
// 左边不是原始类型,尝试把左边转成原始类型,[].valueOf()=[],非原始类型,[].toString()='',返回
// '' == false
// 转成Number
// 0 == 0
  1. ES6一共有5种方法可以遍历对象的属性。

(1)for…in
for…in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。
(2)Object.keys(obj)
Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有Symbol属性。
(5)Reflect.ownKeys(obj)
Reflect.ownKeys返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否可枚举。
以上的5种方法遍历对象的属性,都遵守同样的属性遍历的次序规则。
首先遍历所有属性名为数值的属性,按照数字排序。
其次遍历所有属性名为字符串的属性,按照生成时间排序。
最后遍历所有属性名为Symbol值的属性,按照生成时间排序。
9. 前端性能优化
- 减少http请求次数
多个css js文件合并成一个文件,多个图标放在一张图片上
- 缓存ajax
- 延迟加载,提前加载
- 减少DOM元素数量
- Gzip压缩传输文件
- 使用GET请求
浏览器在实现XMLHttpRequest POST的时候分成两步,先发header,然后发送数据。而GET却可以用一个TCP报文完成请求
- 避免图片空的src
空的图片src仍然会使浏览器发送请求到服务器,空的src被定义为当前页面

straight HTML 
<img src="">

JavaScript 
var img = new Image(); 
img.src = "";
  • 减小cookie大小
  • 样式表css文件置顶
    将样式表(css)放在网页的HEAD中会让网页显得加载速度更快,因为这样做可以使浏览器逐步加载已经下载的有样式的网页内容。如果将样式表放在底部,浏览器会先渲染dom结构,解析了样式表又重新渲染网页。
  • 避免css表达式
  • <link>代替@import
    避免使用@import的原因很简单,因为它相当于将css放在网页内容底部。
  • 脚本置底
    把脚本置底,这样可以让网页渲染所需要的内容尽快加载显示给用户。
    现在主流浏览器都支持defer关键字,可以指定脚本在文档加载后执行。
    HTML5中新加了async关键字,可以让脚本异步执行。
    <script src="script.js"></script>
    没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。
    <script async src="script.js"></script>
    有 async,加载和渲染后续文档元素的过程将和 script.js 的加载并行进行(异步),js文件加载完立即执行,阻塞后续文档元素。async不是按加载顺序执行
    <script defer src="myscript.js"></script>
    有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。defer按照加载顺序执行
    这里写图片描述
    蓝色线代表下载,红色线代表解析执行,这俩都是针对脚本的;绿色线代表 HTML 解析。
  • 使用外部css和js文件
    使用外部Javascript和CSS文件可以使这些文件被浏览器缓存,从而在不同的请求内容之间重用
  • 压缩css和js文件
  • 减少DOM访问
    缓存已经访问过的元素
    Offline更新节点然后再加回DOM Tree
    避免通过Javascript修复layout
  • 事件委托

    1. onchange onpropertychange 和oninput事件
      onchange事件在内容改变(两次内容有可能还是相等的)且失去焦点时触发;onpropertychange事件却是实时触发,即每增加或删除一个字符就会触发,通过js改变也会触发该事件,但是该事件IE专有。

oninput事件是IE之外的大多数浏览器支持的事件,在value改变时触发,实时的,即每增加或删除一个字符就会触发,然而通过js改变value时,却不会触发;onpropertychange事件是任何属性改变都会触发的,而oninput却只在value改变时触发,oninput要通过addEventListener()来注册,onpropertychange注册方式跟一般事件一样。(此处都是指在js中动态绑定事件,以实现内容与行为分离)

oninput与onpropertychange失效的情况:
(1)oninput事件:a). 当脚本中改变value时,不会触发;b).从浏览器的自动下拉提示中选取时,不会触发。
(2)onpropertychange事件:当input设置为disable=true后,onpropertychange不会触发。

var ie = !!window.ActiveXObject;  
if(ie){  
    object.onpropertychange = fn;  
}else{  
    object.addEventListener("input",fn,false);  
}  
  1. web安全
    XSS 全称“跨站脚本”,是注入攻击的一种。其特点是不对服务器端造成任何伤害,而是通过一些正常的站内交互途径,例如发布评论,提交含有 JavaScript 的内容文本。这时服务器端如果没有过滤或转义掉这些脚本,作为内容发布到了页面上,其他用户访问这个页面的时候就会运行这些脚本。

    CSRF (Cross-site request forgery)
    form 发起的 POST 请求并不受到 CORS 的限制,因此可以任意地使用其他域的 Cookie 向其他域发送 POST 请求,形成 CSRF 攻击

    原理:1、你在一个网站上登录过有cookie信息。2、你再访问别的网站进行操作的时候 ,这个网站向你之前的网站发送请求。

    当你访问 fuck.com 黑客页面的时候,页面上放了一个按钮或者一个表单,URL/action 为http://you.com/delete-myself,这样引导或迫使甚至伪造用户触发按钮或表单。在浏览器发出 GET 或 POST 请求的时候,它会带上 you.com 的 cookie,如果网站没有做 CSRF 防御措施,那么这次请求在 you.com 看来会是完全合法的,这样就会对 you.com 的数据产生破坏。

在讨论如何抵御 CSRF 之前,先要明确 CSRF 攻击的对象,也就是要保护的对象。从以上的例子可知,CSRF
攻击是黑客借助受害者的 cookie 骗取服务器的信任,但是黑客并不能拿到 cookie,也看不到 cookie的内容。另外,对于服务器返回的结果,由于浏览器同源策略的限制,黑客也无法进行解析。因此,黑客无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。所以,我们要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行CSRF 的保护。比如银行系统中转账的请求会直接改变账户的金额,会遭到 CSRF攻击,需要保护。而查询余额是对金额的读取操作,不会改变数据,CSRF 攻击无法解析服务器返回的结果,无需保护。
这里写图片描述
解决方法:
- 给所有请求加上 token 检查。token 一般是随机字符串,只需确保其不可预测性即可。token 可以在 QueryString、POST body 甚至是 Custom Header 里,但千万不能在 Cookies 里。因为cookie发送请求的时候会自动增加上,但是token却不会,这样就避免了攻击

当客户端请求页面时,服务器会生成一个随机数Token,并且将Token放置到session当中,然后将Token发给客户端(一般通过构造hidden表单)。下次客户端提交请求时,Token会随着表单一起提交到服务器端。不过,如果应用于”防止表单重复提交”,服务器端第一次验证相同过后,会将session中的Token值更新下,若用户重复提交,第二次的验证判断将失败,因为用户提交的表单中的Token没变,但服务器端session中Token已经改变了。

  • 检查 referer (请注意,这往往不能防御来自网站自身的 CSRF 攻击,如用户评论中的 <img> 就是一个常见触发点)页面来源的判断
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值