zoukankan      html  css  js  c++  java
  • js学习笔记知识点

    AJAX  

    用法

    AJAX不是JavaScript的规范,它只是一个哥们“发明”的缩写:Asynchronous JavaScript and XML,意思就是用JavaScript执行异步网络请求
    在现代浏览器上写AJAX主要依靠XMLHttpRequest对象:

    1. 'use strict';
    2. function success(text){
    3. var textarea = document.getElementById('test-response-text');
    4. textarea.value = text;
    5. }
    6. function fail(code){
    7. var textarea = document.getElementById('test-response-text');
    8. textarea.value ='Error code: '+ code;
    9. }
    10. var request =newXMLHttpRequest();// 新建XMLHttpRequest对象
    11. request.onreadystatechange =function(){// 状态发生变化时,函数被回调
    12. if(request.readyState ===4){// 成功完成
    13. // 判断响应结果:
    14. if(request.status ===200){
    15. // 成功,通过responseText拿到响应的文本:
    16. return success(request.responseText);
    17. }else{
    18. // 失败,根据响应码判断失败原因:
    19. return fail(request.status);
    20. }
    21. }else{
    22. // HTTP请求还在继续...
    23. }
    24. }
    25. // 发送请求:
    26. request.open('GET','/api/categories');
    27. request.send();
    28. alert('请求已发送,请等待响应...');

    对于低版本的IE,需要换一个ActiveXObject对象:

    1. 'use strict';
    2. function success(text){
    3. var textarea = document.getElementById('test-ie-response-text');
    4. textarea.value = text;
    5. }
    6. function fail(code){
    7. var textarea = document.getElementById('test-ie-response-text');
    8. textarea.value ='Error code: '+ code;
    9. }
    10. var request =newActiveXObject('Microsoft.XMLHTTP');// 新建Microsoft.XMLHTTP对象
    11. request.onreadystatechange =function(){// 状态发生变化时,函数被回调
    12. if(request.readyState ===4){// 成功完成
    13. // 判断响应结果:
    14. if(request.status ===200){
    15. // 成功,通过responseText拿到响应的文本:
    16. return success(request.responseText);
    17. }else{
    18. // 失败,根据响应码判断失败原因:
    19. return fail(request.status);
    20. }
    21. }else{
    22. // HTTP请求还在继续...
    23. }
    24. }
    25. // 发送请求:
    26. request.open('GET','/api/categories');
    27. request.send();
    28. alert('请求已发送,请等待响应...');

    如果你想把标准写法和IE写法混在一起,可以这么写:

    1. var request;
    2. if(window.XMLHttpRequest){
    3. request =newXMLHttpRequest();
    4. }else{
    5. request =newActiveXObject('Microsoft.XMLHTTP');
    6. }

    通过检测window对象是否有XMLHttpRequest属性来确定浏览器是否支持标准的XMLHttpRequest。注意,不要根据浏览器的navigator.userAgent来检测浏览器是否支持某个JavaScript特性,一是因为这个字符串本身可以伪造,二是通过IE版本判断JavaScript特性将非常复杂。

    当创建了XMLHttpRequest对象后,要先设置onreadystatechange的回调函数。在回调函数中,通常我们只需通过readyState === 4判断请求是否完成,如果已完成,再根据status === 200判断是否是一个成功的响应。

    XMLHttpRequest对象的open()方法有3个参数,第一个参数指定是GET还是POST,第二个参数指定URL地址,第三个参数指定是否使用异步,默认是true,所以不用写。

    注意,千万不要把第三个参数指定为false,否则浏览器将停止响应,直到AJAX请求完成。如果这个请求耗时10秒,那么10秒内你会发现浏览器处于“假死”状态。

    最后调用send()方法才真正发送请求。GET请求不需要参数,POST请求需要把body部分以字符串或者FormData对象传进去。


    安全限制

    上面代码的URL使用的是相对路径。如果你把它改为'http://www.sina.com.cn/',再运行,肯定报错。在Chrome的控制台里,还可以看到错误信息。

    这是因为浏览器的同源策略导致的。默认情况下,JavaScript在发送AJAX请求时,URL的域名必须和当前页面完全一致。

    完全一致的意思是,域名要相同(www.example.com和example.com不同),协议要相同(http和https不同),端口号要相同(默认是:80端口,它和:8080就不同)。有的浏览器口子松一点,允许端口不同,大多数浏览器都会严格遵守这个限制。

    那是不是用JavaScript无法请求外域(就是其他网站)的URL了呢?方法还是有的,大概有这么几种:

    一是通过Flash插件发送HTTP请求,这种方式可以绕过浏览器的安全限制,但必须安装Flash,并且跟Flash交互。不过Flash用起来麻烦,而且现在用得也越来越少了。

    二是通过在同源域名下架设一个代理服务器来转发,JavaScript负责把请求发送到代理服务器:

    '/proxy?url=http://www.sina.com.cn'

    代理服务器再把结果返回,这样就遵守了浏览器的同源策略。这种方式麻烦之处在于需要服务器端额外做开发。


    JSONP

    第三种方式称为JSONP,它有个限制,只能用GET请求,并且要求返回JavaScript。这种方式跨域实际上是利用了浏览器允许跨域引用JavaScript资源:

    1. <html>
    2. <head>
    3. <scriptsrc="http://example.com/abc.js"></script>
    4. ...
    5. </head>
    6. <body>
    7. ...
    8. </body>
    9. </html>

    JSONP通常以函数调用的形式返回,例如,返回JavaScript内容如下:

    1. foo('data');

    这样一来,我们如果在页面中先准备好foo()函数,然后给页面动态加一个<script>节点,相当于动态读取外域的JavaScript资源,最后就等着接收回调了。

    以163的股票查询URL为例,对于URL:http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice,你将得到如下返回:

    refreshPrice({"0000001":{"code": "0000001", … });

    因此我们需要首先在页面中准备好回调函数:

    1. function refreshPrice(data){
    2. var p = document.getElementById('test-jsonp');
    3. p.innerHTML ='当前价格:'+
    4. data['0000001'].name +': '+
    5. data['0000001'].price +';'+
    6. data['1399001'].name +': '+
    7. data['1399001'].price;
    8. }

    最后用getPrice()函数触发:

    1. function getPrice(){
    2. var
    3. js = document.createElement('script'),
    4. head = document.getElementsByTagName('head')[0];
    5. js.src ='http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice';
    6. head.appendChild(js);
    7. }

    就完成了跨域加载数据。


    CORS

    如果浏览器支持HTML5,那么就可以一劳永逸地使用新的跨域策略:CORS了。

    CORS全称Cross-Origin Resource Sharing,是HTML5规范定义的如何跨域访问资源。

    了解CORS前,我们先搞明白概念:

    Origin表示本域,也就是浏览器当前页面的域。当JavaScript向外域(如sina.com)发起请求后,浏览器收到响应后,首先检查Access-Control-Allow-Origin是否包含本域,如果是,则此次跨域请求成功,如果不是,则请求失败,JavaScript将无法获取到响应的任何数据。

    用一个图来表示就是:
    廖雪峰

    假设本域是my.com,外域是sina.com,只要响应头Access-Control-Allow-Origin为http://my.com,或者是*,本次请求就可以成功。

    可见,跨域能否成功,取决于对方服务器是否愿意给你设置一个正确的Access-Control-Allow-Origin,决定权始终在对方手中。

    上面这种跨域请求,称之为“简单请求”。简单请求包括GET、HEAD和POST(POST的Content-Type类型
    仅限application/x-www-form-urlencoded、multipart/form-data和text/plain),并且不能出现任何自定义头(例如,X-Custom: 12345),通常能满足90%的需求。

    无论你是否需要用JavaScript通过CORS跨域请求资源,你都要了解CORS的原理。最新的浏览器全面支持HTML5。在引用外域资源时,除了JavaScript和CSS外,都要验证CORS。例如,当你引用了某个第三方CDN上的字体文件时:

    1. /* CSS */
    2. @font-face {
    3. font-family:'FontAwesome';
    4. src: url('http://cdn.com/fonts/fontawesome.ttf') format('truetype');
    5. }

    如果该CDN服务商未正确设置Access-Control-Allow-Origin,那么浏览器无法加载字体资源。

    对于PUT、DELETE以及其他类型如application/json的POST请求,在发送AJAX请求之前,浏览器会先发送一个OPTIONS请求(称为preflighted请求)到这个URL上,询问目标服务器是否接受:

    1. OPTIONS /path/to/resource HTTP/1.1
    2. Host: bar.com
    3. Origin: http://my.com
    4. Access-Control-Request-Method: POST

    服务器必须响应并明确指出允许的Method:

    1. HTTP/1.1 200 OK
    2. Access-Control-Allow-Origin: http://my.com
    3. Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS
    4. Access-Control-Max-Age: 86400

    浏览器确认服务器响应的Access-Control-Allow-Methods头确实包含将要发送的AJAX请求的Method,才会继续发送AJAX,否则,抛出一个错误。

    由于以POST、PUT方式传送JSON格式的数据在REST中很常见,所以要跨域正确处理POST和PUT请求,服务器端必须正确响应OPTIONS请求。


    面向对象编程

    创建对象

    JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。

    当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。

    例如,创建一个Array对象:

    1. var arr =[1,2,3];

    其原型链是:

    arr —-> Array.prototype —-> Object.prototype —-> null

    Array.prototype定义了indexOf()、shift()等方法,因此你可以在所有的Array对象上直接调用这些方法。
    当我们创建一个函数时:

    1. function foo(){
    2. return0;
    3. }

    函数也是一个对象,它的原型链是:

    foo —-> Function.prototype —-> Object.prototype —-> null

    由于Function.prototype定义了apply()等方法,因此,所有函数都可以调用apply()方法。

    很容易想到,如果原型链很长,那么访问一个对象的属性就会因为花更多的时间查找而变得更慢,因此要注意不要把原型链搞得太长。


    构造函数

    除了直接用{ … }创建一个对象外,JavaScript还可以用一种构造函数的方法来创建对象。它的用法是,先定义一个构造函数:

    1. functionStudent(name){
    2. this.name = name;
    3. this.hello =function(){
    4. alert('Hello, '+this.name +'!');
    5. }
    6. }

    你会问,咦,这不是一个普通函数吗?

    这确实是一个普通函数,但是在JavaScript中,可以用关键字new来调用这个函数,并返回一个对象:

    1. var xiaoming =newStudent('小明');
    2. xiaoming.name;// '小明'
    3. xiaoming.hello();// Hello, 小明!

    注意,如果不写new,这就是一个普通函数,它返回undefined。但是,如果写了new,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;。

    新创建的xiaoming的原型链是:

    xiaoming —-> Student.prototype —-> Object.prototype —-> null

    也就是说,xiaoming的原型指向函数Student的原型。如果你又创建了xiaohong、xiaojun,那么这些对象的原型与xiaoming是一样的:

    1. xiaoming ↘
    2. xiaohong -→ Student.prototype ----> Object.prototype ----> null
    3. xiaojun ↗

    new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身:

    1. xiaoming.constructor ===Student.prototype.constructor;// true
    2. Student.prototype.constructor ===Student;// true
    3. Object.getPrototypeOf(xiaoming)===Student.prototype;// true
    4. xiaoming instanceof Student;// true

    看晕了吧?用一张图来表示这些乱七八糟的关系就是:
    廖雪峰

    红色箭头是原型链。注意,Student.prototype指向的对象就是xiaoming、xiaohong的原型对象,这个原型对象自己还有个属性constructor,指向Student函数本身。

    另外,函数Student恰好有个属性prototype指向xiaoming、xiaohong的原型对象,但是xiaoming、xiaohong这些对象可没有prototype这个属性,不过可以用__proto__这个非标准用法来查看。

    现在我们就认为xiaoming、xiaohong这些对象“继承”自Student。

    不过还有一个小问题,注意观察:

    1. xiaoming.name;// '小明'
    2. xiaohong.name;// '小红'
    3. xiaoming.hello;// function: Student.hello()
    4. xiaohong.hello;// function: Student.hello()
    5. xiaoming.hello === xiaohong.hello;// false

    xiaoming和xiaohong各自的name不同,这是对的,否则我们无法区分谁是谁了。

    xiaoming和xiaohong各自的hello是一个函数,但它们是两个不同的函数,虽然函数名称和代码都是相同的!

    如果我们通过new Student()创建了很多对象,这些对象的hello函数实际上只需要共享同一个函数就可以了,这样可以节省很多内存。

    要让创建的对象共享一个hello函数,根据对象的属性查找原则,我们只要把hello函数移动到xiaoming、xiaohong这些对象共同的原型上就可以了,也就是Student.prototype:
    廖雪峰

    修改代码如下:

    1. functionStudent(name){
    2. this.name = name;
    3. }
    4. Student.prototype.hello =function(){
    5. alert('Hello, '+this.name +'!');
    6. };

    用new创建基于原型的JavaScript的对象就是这么简单!

    • 忘记写new怎么办
      如果一个函数被定义为用于创建对象的构造函数,但是调用时忘记了写new怎么办?

    在strict模式下,this.name = name将报错,因为this绑定为undefined,在非strict模式下,this.name = name不报错,因为this绑定为window,于是无意间创建了全局变量name,并且返回undefined,这个结果更糟糕。

    所以,调用构造函数千万不要忘记写new。为了区分普通函数和构造函数,按照约定,构造函数首字母应当大写,而普通函数首字母应当小写,这样,一些语法检查工具如jslint将可以帮你检测到漏写的new。
    最后,我们还可以编写一个createStudent()函数,在内部封装所有的new操作。一个常用的编程模式像这样:

    1. functionStudent(props){
    2. this.name = props.name ||'匿名';// 默认值为'匿名'
    3. this.grade = props.grade ||1;// 默认值为1
    4. }
    5. Student.prototype.hello =function(){
    6. alert('Hello, '+this.name +'!');
    7. };
    8. function createStudent(props){
    9. returnnewStudent(props ||{})
    10. }

    这个createStudent()函数有几个巨大的优点:一是不需要new来调用,二是参数非常灵活,可以不传,也可以这么传:

    1. var xiaoming = createStudent({
    2. name:'小明'
    3. });
    4. xiaoming.grade;// 1

    如果创建的对象有很多属性,我们只需要传递需要的某些属性,剩下的属性可以用默认值。由于参数是一个Object,我们无需记忆参数的顺序。如果恰好从JSON拿到了一个对象,就可以直接创建出xiaoming。


    原型继承

    在传统的基于Class的语言如Java、C++中,继承的本质是扩展一个已有的Class,并生成新的Subclass。

    由于这类语言严格区分类和实例,继承实际上是类型的扩展。但是,JavaScript由于采用原型继承,我们无法直接扩展一个Class,因为根本不存在Class这种类型。

    但是办法还是有的。我们先回顾Student构造函数:
    ···javascript
    function Student(props) {
    this.name = props.name || 'Unnamed';
    }

    Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
    }

    1. 以及Student的原型链:
    2. ![](http://www.liaoxuefeng.com/files/attachments/001439872136313496e60e07ed143bda40a0200b12d8cc3000/l)
    3. 现在,我们要基于Student扩展出PrimaryStudent,可以先定义出PrimaryStudent
    4. ```javascript
    5. function PrimaryStudent(props) {
    6. // 调用Student构造函数,绑定this变量:
    7. Student.call(this, props);
    8. this.grade = props.grade || 1;
    9. }

    但是,调用了Student构造函数不等于继承了Student,PrimaryStudent创建的对象的原型是:

    new PrimaryStudent() —-> PrimaryStudent.prototype —-> Object.prototype —-> null

    必须想办法把原型链修改为:

    new PrimaryStudent() —-> PrimaryStudent.prototype —-> Student.prototype —-> Object.prototype —-> null

    这样,原型链对了,继承关系就对了。新的基于PrimaryStudent创建的对象不但能调用PrimaryStudent.prototype定义的方法,也可以调用Student.prototype定义的方法。

    如果你想用最简单粗暴的方法这么干:

    1. PrimaryStudent.prototype =Student.prototype;

    是不行的!如果这样的话,PrimaryStudent和Student共享一个原型对象,那还要定义PrimaryStudent干啥?

    我们必须借助一个中间对象来实现正确的原型链,这个中间对象的原型要指向Student.prototype。为了实现这一点,参考道爷(就是发明JSON的那个道格拉斯)的代码,中间对象可以用一个空函数F来实现:

    1. // PrimaryStudent构造函数:
    2. functionPrimaryStudent(props){
    3. Student.call(this, props);
    4. this.grade = props.grade ||1;
    5. }
    6. // 空函数F:
    7. function F(){
    8. }
    9. // 把F的原型指向Student.prototype:
    10. F.prototype =Student.prototype;
    11. // 把PrimaryStudent的原型指向一个新的F对象,F对象的原型正好指向Student.prototype:
    12. PrimaryStudent.prototype =new F();
    13. // 把PrimaryStudent原型的构造函数修复为PrimaryStudent:
    14. PrimaryStudent.prototype.constructor =PrimaryStudent;
    15. // 继续在PrimaryStudent原型(就是new F()对象)上定义方法:
    16. PrimaryStudent.prototype.getGrade =function(){
    17. returnthis.grade;
    18. };
    19. // 创建xiaoming:
    20. var xiaoming =newPrimaryStudent({
    21. name:'小明',
    22. grade:2
    23. });
    24. xiaoming.name;// '小明'
    25. xiaoming.grade;// 2
    26. // 验证原型:
    27. xiaoming.__proto__ ===PrimaryStudent.prototype;// true
    28. xiaoming.__proto__.__proto__ ===Student.prototype;// true
    29. // 验证继承关系:
    30. xiaoming instanceof PrimaryStudent;// true
    31. xiaoming instanceof Student;// true

    用一张图来表示新的原型链:

    注意,函数F仅用于桥接,我们仅创建了一个new F()实例,而且,没有改变原有的Student定义的原型链。

    如果把继承这个动作用一个inherits()函数封装起来,还可以隐藏F的定义,并简化代码:

    1. function inherits(Child,Parent){
    2. var F =function(){};
    3. F.prototype =Parent.prototype;
    4. Child.prototype =new F();
    5. Child.prototype.constructor =Child;
    6. }

    这个inherits()函数可以复用:

    1. functionStudent(props){
    2. this.name = props.name ||'Unnamed';
    3. }
    4. Student.prototype.hello =function(){
    5. alert('Hello, '+this.name +'!');
    6. }
    7. functionPrimaryStudent(props){
    8. Student.call(this, props);
    9. this.grade = props.grade ||1;
    10. }
    11. // 实现原型继承链:
    12. inherits(PrimaryStudent,Student);
    13. // 绑定其他方法到PrimaryStudent原型:
    14. PrimaryStudent.prototype.getGrade =function(){
    15. returnthis.grade;
    16. };

    class继承

    在上面的章节中我们看到了JavaScript的对象模型是基于原型实现的,特点是简单,缺点是理解起来比传统的类-实例模型要困难,最大的缺点是继承的实现需要编写大量代码,并且需要正确实现原型链。

    有没有更简单的写法?有!

    新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单。

    我们先回顾用函数实现Student的方法:

    1. functionStudent(name){
    2. this.name = name;
    3. }
    4. Student.prototype.hello =function(){
    5. alert('Hello, '+this.name +'!');
    6. }

    如果用新的class关键字来编写Student,可以这样写:

    1. classStudent{
    2. constructor(name){
    3. this.name = name;
    4. }
    5. hello(){
    6. alert('Hello, '+this.name +'!');
    7. }
    8. }

    比较一下就可以发现,class的定义包含了构造函数constructor和定义在原型对象上的函数hello()(注意没有function关键字),这样就避免了Student.prototype.hello = function () {…}这样分散的代码。

    最后,创建一个Student对象代码和前面章节完全一样:

    1. var xiaoming =newStudent('小明');
    2. xiaoming.hello();
    • class继承
      用class定义对象的另一个巨大的好处是继承更方便了。想一想我们从Student派生一个PrimaryStudent需要编写的代码量。现在,原型继承的中间对象,原型对象的构造函数等等都不需要考虑了,直接通过extends来实现:
    1. classPrimaryStudent extends Student{
    2. constructor(name, grade){
    3. super(name);// 记得用super调用父类的构造方法!
    4. this.grade = grade;
    5. }
    6. myGrade(){
    7. alert('I am at grade '+this.grade);
    8. }
    9. }

    注意PrimaryStudent的定义也是class关键字实现的,而extends则表示原型链对象来自Student。子类的构造函数可能会与父类不太相同,例如,PrimaryStudent需要name和grade两个参数,并且需要通过super(name)来调用父类的构造函数,否则父类的name属性无法正常初始化。

    PrimaryStudent已经自动获得了父类Student的hello方法,我们又在子类中定义了新的myGrade方法。

    ES6引入的class和原有的JavaScript原型继承有什么区别呢?实际上它们没有任何区别,class的作用就是让JavaScript引擎去实现原来需要我们自己编写的原型链代码。简而言之,用class的好处就是极大地简化了原型链代码。

    你一定会问,class这么好用,能不能现在就用上?

    现在用还早了点,因为不是所有的主流浏览器都支持ES6的class。如果一定要现在就用上,就需要一个工具把class代码转换为传统的prototype代码,可以试试Babel这个工具。  

    摘抄来源:http://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000


  • 相关阅读:
    tar打包split分割分解拆分大包文件
    SAP 语言码转换
    SAP audit S41909
    电商收付通系列<1>图片上传API
    Ladon7.4 CVE-2020-0688 Exchange序列化漏洞利用
    [反诈骗]入侵骗子电脑-揭秘冒充企业老板诈骗全过程
    Ladon插件-CVE-2020-1472域控提权漏洞EXP
    Winrm远程命令/端口复用后门/WinrmCmd/密码爆破
    〖教程〗Ladon WinrmExec远程执行命令
    Ladon插件-批量检测网站是否使用Shiro
  • 原文地址:https://www.cnblogs.com/luolei/p/7076256.html
Copyright © 2011-2022 走看看