Coder Social home page Coder Social logo

blog's People

Contributors

zhuzhihang avatar zzhihang avatar

Watchers

 avatar  avatar

Forkers

enterpriseih

blog's Issues

跨域与解决方案

跨域与解决方案

CORS(Cross-Origin Resouce Sharing)

CORS基本**就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

发送一个请求时携带一个Origin头部,其中过包括请求页面的源信息(协议、域名和端口),浏览器之后会根据这个头部信息来决定是否给予响应。
比如: Origin:http://www.baidu.com

如果服务器认为这个请求可以接受,就会在Access-Control-Allow-Origin头部回发相同的源信息(如果是公共资源,可以回发"*")。例如:
Access-Control-Allow-Origin:http://www.baidu.com

如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求。此时的请求和响应都不会包含cookie信息。

请求需要携带凭据怎么处理呢?
通过将withCredentials属性设置为true,可以指定某个请求携带凭据,同样的,如果服务器接受携带凭据的请求,会用下面的http头部响应。
Access-Control-Allow-Credentials: true
如果发送的是带凭据的请求,但服务器的响应中没有包含这个头部,那么浏览器就不会把响应交给JS处理(于是,resourceText中将是空字符串,status值为0,触发onError事件)

JSONP以及封装一个JSONP方法

    function jsonp(url, data, callback){
        var callbackName = "callback_" + new Date().getTime();
        window[callbackName] = callback;
        data.callback = callbackName;
        var params = [];
        for (var key in data){
            params.push(key + '=' + data[key])
        }
    
        var script = document.createElement('script');
        script.src = url + '?' + params.join('&');
    
        window[callbackName] = function (param) {//将传入的callback封装在内部并挂载在window身上
            callback(param);
            document.body.removeChild(script);
        };
    
        document.body.appendChild(script);
    }
    
    
    //返回promise
    function jsonp2(url, data, callback){
        var callbackName = "callback_" + new Date().getTime();
        window[callbackName] = callback;
        data.callback = callbackName;
        var params = [];
        for (var key in data){
            params.push(key + '=' + data[key])
        }
    
        var script = document.createElement('script');
        script.src = url + '?' + params.join('&');
        document.body.appendChild(script);
    
        return new Promise(function (resolve, reject) {
            window[callbackName] = function (param) {
                try {
                    resolve(param);
                    callback(param);
                }catch (e) {
                    reject(param)
                }finally {
                    document.body.removeChild(script);
                }
            };
        });
    }
    
    
    
    jsonp2('https://photo.sina.cn/aj/index',{
        page:1,
        cate:'recommend',
    },function(data){
        console.log(data)
    }).then(function (data) {
        console.log(data)
    })

一些代码运行的过程(继续更新)

Array.toString会对其array每项调用toString方法

[pserson].toString如何返回mr.zhu?
var person = {
  toString: function(){
    return 'mr.zhu'
  }
}

[person].toString() // mr.zhu

为什么一些基本数据类型还能有一些方法?(比如 var s = 'I am a string'; s可以调用String的一些方法?)

基本类型值不是对象,理论上不应该有方法,实际上在s被使用读取的过程中,代码内部会完成以下处理:

  1. 创建String类型实例;
  2. 在实例上调用指定方法;
  3. 销毁这个实例。
    所以以上实际为:
var s = new String('I am a string');

这个new的过程应该都很熟悉

搞懂Event Loop

Event Loop中的宏队列和微队列

宏队列,macrotask,也叫tasks。 一些异步任务的回调会依次进入macro task queue,等待后续被调用,这些异步任务包括:

  • setTimeout
  • setInterval
  • setImmediate (Node独有)
  • requestAnimationFrame (浏览器独有)
  • I/O
  • UI rendering (浏览器独有)

微队列,microtask,也叫jobs。 另一些异步任务的回调会依次进入micro task queue,等待后续被调用,这些异步任务包括:

  • process.nextTick (Node独有)
  • Promise
  • Object.observe
  • MutationObserver

avatar

JavaScript代码的具体流程

  1. 执行全局Script同步代码,这些同步代码有一些是同步语句,有一些是异步语句(比如setTimeout等);
  2. 全局Script代码执行完毕后,调用栈Stack会清空;
  3. 从微队列microtask queue中取出位于队首的回调任务,放入调用栈Stack中执行,执行完后microtask queue长度减1;
  4. 继续取出位于队首的任务,放入调用栈Stack中执行,以此类推,直到直到把microtask queue中的所有任务都执行完毕。注意,如果在执行microtask的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行
  5. microtask queue中的所有任务都执行完毕,此时microtask queue为空队列,调用栈Stack也为空;
  6. 取出宏队列macrotask queue中位于队首的任务,放入Stack中执行;
  7. 执行完毕后,调用栈Stack为空;
  8. 重复第3-7个步骤;
  9. 重复第3-7个步骤;

这里归纳3个重点:

  1. 宏队列macrotask一次只从队列中取一个任务执行,执行完后就去执行微任务队列中的任务;
  2. 微任务队列中所有的任务都会被依次取出来执行,知道microtask queue为空;
  3. 图中没有画UI rendering的节点,因为这个是由浏览器自行判断决定的,但是只要执行UI rendering,它的节点是在执行完所有的microtask之后,下一个macrotask之前,紧跟着执行UI render。

实战

Q1

console.log(1); //同步代码被执行 1

setTimeout(() => {
  console.log(2); //进入宏队列
  Promise.resolve().then(() => {
    console.log(3) //进入微队列
  });
});

new Promise((resolve, reject) => {
  console.log(4) //new Prosmise的时候会执行此处
  resolve(5) 
}).then((data) => { //进入微队列
  console.log(data);
})

setTimeout(() => {//进入宏队列
  console.log(6);
})

console.log(7); //同步代码被执行 7

A1

// 正确答案
1
4
7
5
2
3
6

Q2

console.log(1);  //1

setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => { //注意此处then之后的回调作为微任务被添加到队列的末尾,需要将此处的微任务执行完毕才能执行下一个宏任务队列中的第一项; 
  //谨记这句话 在执行微队列microtask queue中任务的时候,如果又产生了microtask,那么会继续添加到队列的末尾,也会在这个周期执行,直到microtask queue为空停止。
    console.log(3)
  });
});

new Promise((resolve, reject) => {
  console.log(4) //new Prosmise的时候会执行此处 4
  resolve(5)
}).then((data) => {
  console.log(data);
  
  Promise.resolve().then(() => {
    console.log(6)
  }).then(() => {
    console.log(7)
    
    setTimeout(() => {
      console.log(8)
    }, 0);
  });
})

setTimeout(() => {
  console.log(9);
})

console.log(10);

A2

// 正确答案
1
4
10
5
6
7
2
3
9
8

内容大部分学习自 带你彻底弄懂Event Loop

constructor __proto__ prototype的理解

只有function才有prototype属性 默认prototye对象身上只有一个constructor属性指向function
任何对象都有__proto__(原型)属性,都会指向其构造函数的prototype
  • 构造函数的__proto__
所有的构造函数都是Function的实例,包括Array, Function, Date, Object, Number内置构造函数

所以 
Array.__proto__ === Function.prototype
Object.__proto__ === Function.prototype
Function.__proto__ === Function.prototype

var a = function(){}

a.__proto__ === Function.prototype 因为a也是Function的实例

  • 普通对象的__proto__
var a = new Object();
a._proto = Object.prototype
所有的普通对象的__proto__都等于Object.prototype,因为所有的普通对象都可以看做是由构造函数构造出来的对象
    
  • 顶层Object.proto.prototype === null
原型链是向上搜索的,搜到最顶层没有的话就会返回undefined了
    
new的过程(new Object())
  • 创建返回值 既然是得到对象 也就是创建一个对象作为new的返回实例,这里设为a
  • 原型指向 对象肯定是要有__proto__原型的,所有原型都指向其构造函数的prototype,也就是进行了a.proto = Object.prototype赋值操作;
  • this指向处理 将this指向a,也就是指向构造函数的实例
  • 执行构造函数内部代码

经过上面步骤,生成的a就有了构造函数的属性和方法

代码如何实现呢?(可以和上面new的过程自行连线)

function myNew(){
    var obj = new Object();
    var Constructor = [].shift.call(arguments)
    obj._proto_ = Constructor.prototype;
    var ret = Constructor.apply(obj, arguments);
    return typeof ret === 'object' ? ret : obj; //处理构造函数是否有返回值
}

constructor

既然每一个实例对象的原型都可以访问到其构造函数的prototype属性, 因为构造函数prototype本身就保存了自身constructor,constructor指向构造函数方便自己访问自己的构造函数方法

因此

实例.constructor === 构造函数.prototype.constructor 这是实例通过访问自己的__proto__属性从而访问到其构造函数的prototype 自然也就拿到了constructor

简单的子类继承父类

function Base() {}

// Sub1 inherited from Base through prototype chain
function Sub1(){}
Sub1.prototype = new Base();
Sub1.prototype.constructor = Sub1;

Sub1.superclass = Base.prototype;

// Sub2 inherited from Sub1 through prototype chain
function Sub2(){}
Sub2.prototype = new Sub1();
Sub2.prototype.constructor = Sub2;

Sub2.superclass = Sub1.prototype;

// Test prototype chain
alert(Sub2.prototype.constructor);// function Sub2(){}
alert(Sub2.superclass.constructor);// function Sub1(){}
alert(Sub2.superclass.constructor.superclass.constructor);// function Base(){}

上面代码可以看出,一旦prototype整个对象发生重新赋值的话constructor属性就会发生变化

function F() {}
F.prototype = {
_name: 'Eric',
getName: function() {
return this._name;
}
};

初看这种方式并无问题,但是你会发现下面的情况:

var f = new F();
alert(f.constructor === F); // false 
f.constructor === Object // true
f.constructor === Object.prototype.constructor //true

这是因为F.prototype被完全重写了,导致实例f通过__proto__查找到的只是重新赋值的对象字面量{};
由于{}是Object的实例,{}.__proto__指向了Object.prototype,此时原型链向上查找,最终f.constructor === Object.prototype.constructor, 由于prototype.constructor指向自身,所以f.constructor === Object;

恢复constructor指针的指向

只需要F.prorotype.constructor === F就行了

js中创建对象的几种方式

js中创建对象的几种方式

工厂模式

function createPig(weight, age){
	var p = {};
	p.weight = weight;
	p.age = age;
	p.sayWeight = function(){
		return this.weight;
	}
}
var person = createPig('zhu', 18);

优点:对象的封装,解决了创建相似对象的问题;
缺点:无法知道对象的类型,对象类型p始终继承自Object,原型链上只有Object.prototype。

构造函数模式

function Pig(weight, age){
	this.weight = weight;
	this.age = age;
	this.sayWeight = function(){
		return this.weight;
	}
}

var pig = new Pig(100, 1);

优点:能够识别对象类型,pig来自于其构造函数Pig;
缺点:实例会复制每个sayHungry方法,函数也是对象,相当于同时又实例化了多个对象。

原型模式 原型链及继承原理

function Pig(){

}

Pig.prototype.weight = '10';

Pig.prototype.sayWeight = function(){
	return this.weight;
}

优点: 个人觉得没什么优点,反而写的更麻烦了,因为属性也是同样被共享了;
缺点:一般很少有人这样用吧,构造函数初始化的时候传入一些参数更好,于是也就有了构造函数+原型链的方法模式

构造函数 + 原型链模式

function Pig(weight, age){
	this.weight = weight;
	this.age = age;
}

Pig.prototype = {
	constructor: Pig;
	sayWeight: function(){
		return this.weight;
	}
}

优点:灵活,共享属性和方法以及实例各自的属性都会被区分;

寄生构造函数模式

基本**为创建一个函数,函数的作用仅仅只是将创建对象的代码封装起来,然后返回新创建的对象,表面看来,和第一种模式很像;
function Person(weight, age){
var pig = {};
pig.weight = weight;
pig.age = age;
return pig;
}

假设有这样一个问题,在不破坏Array本身构造函数的情况下,如何创建一个同样拥有Array方法并可以扩展?

function MyArray(){
    var array = [];
    array.push.apply(array, arguments);
    array.splitByLine = function(){
        return this.join('|');
    }
    return array;		
}
var myArray = new MyArray('zhu', 'zhi')
console.log(myArray.splitByLine());

手动实现call和apply

var foo = {
    value: 1
};

function bar(name, age) {
    console.log(this.value)
    console.log(name)
    console.log(age)
    return {
        name: name
    };
}


//bar.call(foo)


Function.prototype.myCall = function (context) {
    context = context || window; //传入null的时候指向window
    var args = [];
    for (var i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i + ']') //使用字符串的原因是防止eval('context.fn(zhuzhihang, 18)'),会报zhuzhihang is not defined
    }
    //es6在此处就很简单了
    // args = [...arguments].slice(1);
    // context.fn(...args)
    console.log(args) //[ 'arguments[1]', 'arguments[2]' ],这样在执行eval时,arguments[1]是作为一个真实变量存在的,
    context.fn = this;
    var result = eval('context.fn(' + args + ')');
    delete context.fn;
    return result;
};

var result = bar.myCall(foo, 'zhuzhihang', '18')
console.log(result)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.