Recently I am on vacation, and I plan to review the conscience textbook “JavaScript Advanced Programming” (3rd edition) behind closed doors for several days. At present, I have entered the front end field for nearly two years. Now I am re-reading and recording the “hard” knowledge points of this textbook 😊.

Function not overloaded

ECMAScript functions cannot be overloaded in the traditional sense. In other languages, such as Java, it is possible to write two definitions for a function, as long as the signatures (the types and numbers of arguments that are accepted) are different [P66]. ECMAScript types are loose and have no signature, so they are not overloaded.

function load(num){ return num + 100; } function load(num,name){ return num + 200; } var result = load(100); // the function declaration after 300 # overwrites the function declaration before itCopy the code

Basic data types

Primitive type values refer to simple data segments, whereas reference types refer to objects that may consist of multiple values [P68]. Here pointed out the basic data types is that the es5 ha: Undefined, Null, Boolean, Number, and String.

Passing parameters

All functions in ECMAScript have arguments passed by value [p70]. That is, copying a value from outside a function to an argument inside a function is like copying a value from one variable to another. ** A value of a primitive type is passed as a copy of a primitive variable, and a value of a reference type is passed as a copy of a reference variable. ** Below are separate examples of why the two different types are passed by value.

Base type value

Basic type this is easier to understand by value, directly copy the value of the variable passed:

function addTen(num){
	num += 10;
	return num;
}
var count = 20;
var result = addTen(count);
console.log(result); / / 30
console.log(count); // 20, no change
Copy the code

Reference type value

Some people think that the reference type is passed by reference, so they are correct, but what about the following example?

function setName(obj){
	obj.name = 'jiaming';
	obj = new Object(a); obj.name ='Pang Jiaming';
}
var person = new Object(a); setName(person);console.log(person.name); // 'Jiaming', why not 'Pang Jiaming'?
Copy the code

If obj = new Object() is an Object that points to the contents of the heap, then changing the value of its name attribute should take effect, but it doesn’t. So it also passes drops by value.

Function declarations and function expressions

The parser does not treat function declarations and function expressions equally when loading data into the execution environment [p111]. The parser first reads the function declaration and makes it available (accessible) before any code is executed; In the case of functional expressions, they are not actually parsed until the parser reaches the line of code on which they reside.

console.log(sum(10 , 10)); / / 20
function sum(num1 , num2){
	return num1 + num2;
}
Copy the code
console.log(sum(10 , 10)); //TypeError: sum is not a function
var sum = function(num1 , num2){
	return num1 + num2;
}
Copy the code

The apply and call

Each function contains two non-inherited methods: apply() and call(). The purpose of both methods is to call a function in a particular scope, essentially setting the value of this object in the function body [116]. Call and apply are useful in objects.

The apply() and call() methods do the same thing, but the difference is how they receive arguments.

apply

The apply() method takes two arguments: a scope in which the function is run and an Array of arguments, which can be instances of Array or arguments objects (array-like objects).

function sum(num1 , num2){
	return num1 + num2;
}
function callSum1(num1,num2){
	return sum.apply(this.arguments); // Pass arguments as an array object
}
function callSum2(num1,num2){
	return sum.apply(this,[num1 , num2]); // Pass in the array
}
console.log(callSum1(10 , 10)); / / 20
console.log(callSum2(10 , 10)); / / 20
Copy the code

call

The call() method receives the same first argument as the apply() method, except that the rest of the arguments are passed directly to the function. In other words, when using the call() method, the arguments passed to the function must be enumerated one by one.

function sum(num1 , num2){
	return num1 + num2;
}
function callSum(num1 , num2){
	return sum.call(this , sum1 , sum2);
}
console.log(callSum(10 , 10)); / / 20
Copy the code

Create an object

Although Object constructors or Object literals can be used to create a single Object, there is a significant drawback to these approaches: creating many objects using the same interface creates a lot of duplicate code. [p144]

The factory pattern

The factory model is to build a mold and produce objects.

 function createPerson(name , age ,job){
 	var o = new Object(a); o.name = name; o.age = age; o.job = job; o.sayName =function(){
 		alert(this.name);
 	};
 	return o;
 }
 
 var person1 = createPerson('nicholas' , 29 , 'software engineer');
 var person2 = createPerson('greg' , 27 , 'doctor');
Copy the code

The factory pattern solves the problem of creating multiple similar objects (a lot of repetitive code when creating objects), but it does not solve the problem of object recognition (how to know if an object is a Person or Animal).

Constructor pattern

Let’s use constructors to create objects of a specific type. Here is the Person type:

function Person(name , age , job){ // Note that the constructor starts with a capital letter
	this.name = name;
	this.age = age;
	this.job = job;
	this.sayName = function(){
		alert(this.name); }}var person1 = new Person('nicholas' , 29 , 'software engineer');
var person2 = new Person('greg' , 27 , 'doctor');

alert(person1.constructor == Person); // True means that the creator of person1 is Person, the object's type Person
Copy the code

When creating a new instance of Person, you must use the new operator. Calling the constructor in this way actually goes through four steps:

  1. Create a new object
  2. Assign the constructor’s scope to the new object (so this refers to the new object)
  3. Execute the code in the constructor (add attributes to this new object)
  4. Return a new object

Constructors solve the problem of duplicate instances (that is, creating multiple similar objects) and solve the problem of object recognition. However, as above, the methods shared by person1 and person2 are created at instantiation time, which is redundant. Of course you can extract the common methods outside, like this:

function Person(name , age , job){
	this.name = name;
	this.age = age;
	this.job = job;
	this.sayName = sayName;
}
function sayName(){
	alert(this.name);
}
var person1 = new Person('nicholas' , 29 , 'software engineer');
var person2 = new Person('greg' , 27 , 'doctor');
Copy the code

Extracting sayName from a global method is not recommended because it is only used when the Person class creates objects.

The prototype pattern

Each function created has a Prototype property, which is a pointer to an object whose purpose is to contain properties and methods that can be shared by all instances of a particular type.

function Person(){
}
Person.prototype.name = 'nicholas';
Person.prototype.age = 29;
Person.prototype.sayName = function(){
	alert(this.name);
};

var person1 = new Person();
person1.sayName(); // nicholas

var person2 = new Person();
person2.sayName(); // nicholas

console.log(person1.sayName == person2.sayName); // true
Copy the code

The diagram can be as follows:

Person.prototype={} is not recommended as a literal, but it overrides the original Person.prototype object so that the constructor property points to Ohject instead of Person. This can be handled, of course, by pointing correctly and specifying the enumerable property of ‘Construtor’ as Enumerable: False.

The prototype pattern solves the problem of function sharing, but it also presents a problem: the object properties in the instantiation are independent, whereas the prototype pattern is shared here.

Use a combination of constructor and stereotype patterns

The most common way to create custom types is to use a combination of the constructor pattern and the stereotype pattern. The constructor pattern is used to define instance properties, while the stereotype pattern is used to define method and shared properties.

function Person(name , age ,job){
	this.name = name;
	this.age = age;
	this.job = job;
	this.friends = ['shelby' , 'court'];
}
Person.prototype.sayName = function(){
	alert(this.name);
}

var person1 = new Person('nicholas' , 29 , 'software engineer');
var person2 = new Person('greg' , 27 , 'doctor');

person1.friends.push('van');
console.log(person1.friends); // 'shelby,court,van'
console.log(person2.friends); // 'shelby,court'
console.log(person1.friends === person2.friends); // false
console.log(person1.sayName === person2.sayName); // true
Copy the code

Dynamic prototype pattern

Other OO languages, such as Java, create classes that contain their own properties and methods, as well as shared properties and methods, as shown in the following example:

public class Dog{
	int age;
	public Dog(String name ){
		this.age = age;
		System.out.println('The dog's name is:' + name);
	}
	public void setAge(int age){
		age = age;
	}
	public int getAge(a){
		System.out.println('The age of the puppy is:' + age);
		return age;
	}
	
	public static void main(String []args){
		/* Create an object */
		Dog dog = new Dog('tom');
		/* Set the age */ method
		dog.setAge(2);
		/* Call another method to get age */
		dog.getAge();
		/* Can also pass objects. Attribute name gets */
		System.out.println('Variable value:'+ dog.age); }}Copy the code

In order to look like a class, the dynamic stereotype pattern encapsulates all information in the constructor, while preserving the advantage of using both the constructor and the stereotype by initializing the stereotype in the constructor (only when necessary). As follows:

function Person(name , age ,job){
	/ / property
	this.name = name;
	this.age = age;
	this.job = job;
	/ / method
	if(typeof this.sayName ! ='function'){
		Person.prototype.sayName = function(){
			alert(this.name); }}}var friend = new Person('nicholas' , 29 , 'software engineer');
friend.sayName();
Copy the code

Parasitic constructor pattern

In cases where none of the previous patterns are suitable, the parasitic constructor pattern (used in data structures) can be used, which can be thought of as a combination of the factory pattern and the constructor pattern. The basic idea is to create a function that simply encapsulates the code that created the object and then returns the newly created object.

function Person(name , age , job){
	var o = new Object(a); o.name = name; o.age = age; o.job = job; o.sayName =function(){
		alert(this.name);
	}
	return o;
}

var friend = new Person('nicholas'.29 , 'software engineer');
friend.sayName(); // nicholas
Copy the code

With regard to the parasitic constructor pattern, it is important to note that the object returned has nothing to do directly with the constructor or the stereotype properties of the constructor; That is, the object returned by the constructor is no different from the object created outside the constructor. For this reason, you cannot rely on the Instanceof operator to determine the object type. Because of the above problems, it is recommended not to use this pattern when other patterns are available.

Secure constructor pattern

Secure objects are suitable for use in secure environments where the use of this and new is prohibited, or to prevent data from being altered by other applications, such as Mashup applications. The secure constructor follows a similar pattern to the parasitic constructor, but with two differences: the conscious instance method of the newly created object does not refer to this, and the constructor is not called using the new operator.

function Person(name , age , job){
	// Create the object to return
	var o = new Object(a);// Private variables and functions can be defined here
	
	// Add method
	o.sayName = function(){
		alert(name);  // Do not use this.name
	};
	
	// Return the object
	return o;
}

var friend = Person('nicholas'.29 , 'software engineer'); // Do not use new
friend.sayName(); // 'nicholas'
Copy the code

inheritance

Many OO languages support two types of inheritance: interface inheritance and implementation inheritance. Interface inheritance inherits only method signatures, whereas implementation inheritance inherits the actual methods. Interface inheritance cannot be implemented in ECMAScript because functions have no signatures. ECMAScript only supports implementation inheritance, and implementations rely primarily on prototype chains. [p162]

Prototype chain

The basic idea of stereotype chains is to use stereotypes to make one reference type inherit the properties and methods of another. Review the relationship between constructors, stereotypes, and instances: Each constructor has a stereotype object, which contains a pointer to the constructor, and each instance contains an internal pointer to the stereotype object.

function SuperType(){
	this.property = true;
}
SuperType.prototype.getSuperValue = function(){
	return this.property;
}
function SubType(){
	this.subProperty = false;
}

// Inherit SuperType
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function(){
	return this.subProperty;
}

var instance = new SubType();
console.log(instance.getSuperValue()); // true
Copy the code

The prototype chain in the code above is as follows:

There are two problems with stereotype chain inheritance: one is that the stereotype actually becomes an instance of another type, so that the attributes of the original instance become the attributes of the current stereotype, sharing the attributes. The second is that when creating instances of a subtype, you cannot pass arguments to the constructor of a supertype without affecting all object instances.

Borrowing constructor

Borrowing constructors solves the problem of not passing hamsters to constructors caused by stereotype chain inheritance. The apply() or call() methods are used to perform the constructor on the newly created object.

function SuperType(){
	this.colors = ['red'.'blue'.'green'];
}
function SubType(){
	/ / inherit the SuperType
	SuperType.call(this); / / SuperType. Apply (this) with the effect
}

var instance1 = new SubType();
instance1.color.push('black');
console.log(instance1.colors); // 'red,blue,green,black'

var instance2 = new SubType();
console.log(instance2.colors); // 'red,blue,green'
Copy the code

In the example above, I passed no arguments in the parent constructor, so you can experiment by adding arguments yourself.

Borrowing constructors solves the problem of determining the inheritance of stereotype chains, but does not take advantage of the advantage of stereotype chains: sharing. The following combinatorial inheritance combines a stereotype chain and a borrowed constructor, accommodating the best of both.

Combination of inheritance

The idea of composite inheritance is to use the stereotype chain to inherit the stereotype attributes and methods, and to borrow the constructor to inherit the instance attributes.

function SuperType(name){
	this.name = name;
	this.colors = ['red'.'blue'.'green'];
}
SuperType.prototype.sayName = function(){
	console.log(this.name);
}
function SubType(name,age){
	// Inherit attributes
	SuperType.call(this,name);
	this.age = age;
}

// Inheritance method
SubType.prototype = new SuperType();
SubType.prototype.constructor =SubType; // Avoid overwriting constructor pointing errors
SubType.prototype.sayAge = function(){
	console.log(this.age);
}

var instance1 = new SubType('nicholas' , 29);
instance1.colors.push('black');
console.log(instance1.colors); // 'red,blue,green,black'
instance1.sayName(); // 'nicholas'
instance1.sayAge(); / / 29

var instance2 = new SubType('greg' , 27);
console.log(instance2.colors); // 'red,blue,green'
instance2.sayName(); // 'greg'
instance2.sayAge(); / / 27
Copy the code

Composite inheritance avoids the defects of stereotype chains and borrowed constructors, and combines their advantages to become the most common inheritance pattern in JavaScript. Also, instanceof and isPrototypeOf() can be used to identify objects created based on composite inheritance.

Primary inheritance

Archetypal inheritance is the ability to create new objects based on existing objects without having to create custom types.

function object(o){ // Pass in an object
	function F(){};
	F.prototype = o;
	return new F();
}

var person = {
	name : 'nicholas'.friends: ['shelby'.'court'.'van']};var anotherPerson = object(person);
anotherPerson.name = 'greg';
anotherPerson.friends.push('rob');

var yetAnotherPerson = object(person);
yetAnotherPerson.name = 'linda';
yetAnotherPerson.friends.push('barbie');

console.log(person.friends); // 'shelby,court,van,rob,barbie'
Copy the code

Parasitic inheritance

Parasitic inheritance is closely related to prototype inheritance. The idea of parasitic inheritance is similar to that of the parasitic constructor and factory pattern, in that you create a function that just encapsulates the inheritance process, enhances the object internally in some way, and then returns the object as if it had really done all the work.

function object(o){ // Pass in an object
	function F(){};
	F.prototype = o;
	return new F();
}
function createAnother(original){
	var clone = object(original);
	clone.sayHi = function(){
		console.log('hi');
	};
	return clone;
}
var person = {
	name : 'nicholas'.friends : ['shelby'.'court'.'van']}var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // 'hi'
Copy the code

In the example above, the new object anotherPerson not only has all the attributes and methods of Person, but also has its own sayHi() method.

Parasitic combinatorial inheritance

Composite inheritance is the most common inheritance pattern in JavaScript. However, it has its own disadvantages. The biggest problem with composite inheritance is that in any case, the supertype constructor is called twice: once when the subtype stereotype is created and once inside the subtype constructor. Parasitic combinatorial inheritance solves this problem.

Parasitic combinatorial inheritance inherits properties by borrowing constructors and inherits methods through a hybrid form of prototype chains. The basic idea behind this is that you don’t need to call the constructor of the supertype to specify the stereotype of the subtype; all you need is a copy of the stereotype of the supertype. The basic mode of parasitic combinatorial inheritance is as follows:

function inheritPrototype(subType,superType){
	var prototype = Object(superType.prototype); // Create an object
	prototype.constructor = subType; // Enhance the object to prevent overriding the constructor property as follows
	subType.prototype = prototype; // Specify the object
	
}
Copy the code

A complete example is as follows, illustrated in the book [P173]:

function inheritPrototype(subType,superType){
	var prototype = Object(superType.prototype);
	prototype.constructor = subType;
	subType.prototype = prototype;
	
}
function SuperType(name){
	this.name = name;
	this.colors = ['red'.'blue'.'green'];
}
SuperType.prototype.sayName = function(){
	alert(this.name);
}
function SubType(name, age){
	SuperType.call(this, name); // The supertype constructor is called only once here
        this.age = age;
}

inheritPrototype(SubType , SuperType);

SubType.prototype.sayAge = function(){
	console.log(this.age);
}

var instance = new SubType('nicholas' , 29);
Copy the code

The efficiency of the above example is that it calls the SuperType constructor only once and avoids creating unnecessary, redundant properties on subtype. prototype. At the same time, the prototype chain stays the same; So instanceof and inPrototypeOf() can also be used normally. Parasitic combinatorial inheritance is generally considered by developers to be the ideal inheritance paradigm for reference types.

closure

Closures are functions that have access to variables in the scope of another function. A function inside a function uses a variable in the outer function to extend the lifetime of the variable, resulting in resident memory. For example:

function foo(){
    var a = 2;
    return function(){
		a += 1;
		console.log(a); }}var baz = foo();

baz(); / / 3
baz(); / / 4
baz(); / / 5
baz(); / / 6
Copy the code

In the example above, the external function foo() should normally be destroyed after execution, but the internal returned anonymous function that uses the variable cannot be destroyed. If you need to destroy it, you can rewrite it as follows:

function foo(){
	var a = 2;
	return function(){
		a += 1;
		console.log(a); }}var baz = foo();
baz(); / / 3

baz = null; // Set the internal anonymous function to null
Copy the code

Start with closures

Speaking of closures, I was reminded of an article I read on Zhihu not long ago. Self-organized as follows:

for(var i = 0 ; i < 5; i++){
	setTimeout(function(){
		console.log(i);
	},1000)}console.log(i);

/ / 5,5,5,5,5,5
Copy the code

The code above prints six 5’s, executed by first printing console.log(I) in the global output, and then, after a second, five 5’s in a flash. The above code can be interpreted in the narrow sense (ES5) : synchronous => asynchronous => callback (callback is also asynchronous, so I specify the narrow sense here). A synchronous for is executed, and an asynchronous setTimeout(setTimeout and setInterval are asynchronous) is queued, followed by the global console.log(I), and the asynchronous queue is executed.

Question 1: Closures

Rewrite the code above and expect the output to be: 5 => 0,1,2,3,4. Transformation method 1:

for(var i = 0; i < 5; i++){
	(function(j){
		setTimeout(function(){
			console.log(j);
		},1000);
	})(i);
}
console.log(i);

/ / 5,0,1,2,3,4
Copy the code

The code above cleverly uses IIFE(Immediately Invoked Function Expression: the Function Expression declared is executed) to solve the problem caused by the closure. The closure is explained above.

Method two: using the basic type of JS parameter transfer is by value transfer characteristics, the transformation code is as follows

var output = function(i){
	setTimeout(function(){
		console.log(i);
	},1000);
};
for(var i = 0; i < 5; i++){
	output(i); // The I value passed here is copied
}
console.log(i);

/ / 5,0,1,2,3,4
Copy the code

Both of the above methods print 5 after executing the code, followed by 0,1,2,3,4 after a second.

If you don’t think about console.log(I) in the global output of 5, but in the loop output of 0,1,2,3,4. You can also use ES6’s let block-level scoping syntax to make it super simple:

for(let i = 0; i < 5; i++){
	setTimeout(function(){
		console.log(i);
	},1000);
}

/ / 0,1,2,3,4
Copy the code

The top is 0,1,2,3,4 after a second. This is analogous to adding a closure invisibly. So how do you implement 5,0,1,2,3,4 using ES6 syntax?

Ask 2: ES6

Modify the original code so that the output is 0,1,2,3,4 every second, and about 5 every fifth second.

When not using ES6:

for(var i = 0; i < 5; i++){
	(function(j){
		setTimeout(function(){
			console.log(j);
		},1000*j);
	})(i);
}
setTimeout(function(){
	console.log(i);
},1000*i);

/ / 0,1,2,3,4,5
Copy the code

The code above is simple and crude, but not recommended. Output a value every second, and then call back for a final output of 5. Use the ES6 syntax to consider this, and use the Promise scheme:

const tasks = [];
for(var i = 0; i < 5; i++){// the I declaration cannot be changed to let
	((j) = >{
		tasks.push(new Promise((resolve) = >{ / / perform the tasks
			setTimeout((a)= >{
				console.log(j);
				resolve(); // Resolve must be used, otherwise the code will not execute as expected
			},1000*j);
		}))
	})(i);
}

Promise.all(tasks).then((a)= >{ // Execute tasks and call back
	setTimeout((a)= >{
		console.log(i);
	},1000);
});

// Output every second
/ / 0,1,2,3,4,5
Copy the code

If LET is used, my transformation is as follows:

const tasks = [];
for (let i = 0; i < 5; i++) {
		tasks.push(new Promise((resolve) = > {
			setTimeout((a)= > {
				console.log(i);
				resolve();
			}, 1000 * i);
		}));
}

Promise.all(tasks).then((a)= > {
	setTimeout((a)= > {
		console.log(tasks.length);
	}, 1000);
});

/ / 0,1,2,3,4,5
Copy the code

The above code is more complex, can be its granular words, modular. The above two codes with var are modified as follows:

const tasks = []; // Where to store promises for asynchronous operations
const output = (i) = > new Promise((resolve) = > {
	setTimeout((a)= >{
		console.log(i);
	},1000*i);
});

// Generate all asynchronous operations
for(var i = 0; i < 5; i++){
	tasks.push(output(i));
}
// After the asynchronous operation is complete, print the last I
Promise.all(tasks).then((a)= > {
	setTimeout((a)= > {
		console.log(i);
	},1000);
});

// Output every second
/ / 0,1,2,3,4,5
Copy the code

Ask 3: ES7

Since ES6 promises can be written, can ES7 make the code more concise and readable? Then you are using the async await feature of asynchronous operations.

// Emulates sleep in other languages, which can be virtually any asynchronous operation
const sleep = (time) = > new Promise((resolve) = > {
	setTimeout(resolve , time);
});

(async () = > {
	for(var i = 0; i < 5; i++){
		await sleep(1000);
		console.log(i);
	}
	
	await sleep(1000);
	console.log(i); }) ();// Output every second
/ / 0,1,2,3,4,5
Copy the code

Browser window location

Internet Explorer, Safari, Opera, and Chrome all provide screenLeft and screenTop properties that represent the position of the browser window relative to the top left and top of the screen, respectively [P197]. Firefox uses screenX and screenY properties. To be compatible with all browsers, write:

var leftPos = (typeof window.screenLeft == "number")?window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == "number")? window.screenTop : window.screenY;
Copy the code

Browser window size

Due to browser vendor and history issues, the size of the browser itself cannot be confirmed, but the viewport size can be obtained [p198]. As follows:

var pageWidth = window.innerWidth,
    pageHeight = window.innerHeight;
    
if(typeofpageWidth ! ="number") {if(document.compatMode == 'CSS1Compat') {// Lower version of Ie in standard mode
		pageWidth = document.documentElement.clientWidth;
		pageHeight = document.documentElement.clientHeight;
	}else{ // Chrome in promiscuous mode
		pageWidth = document.body.clientWidth;
		pageHeight = document.body.clientHeight; }}Copy the code

The above example could be abbreviated as follows:

var pageWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientHeight;
var pageHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
Copy the code

Transformation in Canvas

Applying transformations for the context of the drawing results in different results with different transformation matrices applied to the processing. [p453]

The transformation matrix can be modified by:

  • Rotation (Angle): Rotate the image Angle radians around the origin
  • scale(scaleX,scaleY)
  • Translate (x,y): Move the origin of the coordinate to (x,y). After performing this transformation, the coordinate (0,0) becomes the point previously represented by (x,y).

JSON

The most important thing to understand about JSON is that it is a data format, not a programming language.

Object literals compared to JSON formats

Let’s look at the object literal demo:

var person = { name : "nicholas", age : 29 }; Var person = {"name" : "Nicholas ", "age" : 29};Copy the code

The above object would look like this if it were written as data:

{"name": "Nicholas ", "age": 29} # Check at https://www.bejson.com/Copy the code

⚠️ JSON objects differ from JavaScript object literals in two other ways. First, no variables are declared (there is no concept of variables in JSON). Second, there is no semicolon (because this is not a JavaScript statement, you don’t need a semicolon). Note that the attributes of the object must be quoted in double (not single) quotes, which is mandatory in JSON.

Stringify () and the parse ()

Read it this way: json.stringify () parses from an object to JSON data format, while json.parse () parses from a string to JSON data format.

var person = {
	name: 'nicholas'.age: 29
};

var jsonText = JSON.stringify(person);

console.log(jsonText);

// {"name":"nicholas","age":29}
Copy the code
var strPerson = '{"name":"nicholas","age":29}';
var jsonText = JSON.parse(strPerson);

console.log(jsonText); // { name: 'nicholas', age: 29 }
Copy the code

The XMLHttpRequest object

The XMLHttpRequest object is used to exchange data with the server in the background. It is the core of Ajax technology [P571].

The XMLHttpRequest object enables you to:

  • Update a web page without reloading the page
  • Request data from the server after the page has loaded
  • Receives data from the server after the page has been loaded
  • Sends data to the server in the background

Use XMLHttpRequest:

Function createXHR(){if(typeof XMLHttpRequest! // IE7+ and other browsers support return new XMLHttpRequest(); }else if(typeof ActiveXObject ! = 'undefined'){ if(typeof arguments.callee.activeXString ! = 'string'){var versions = [' msxml2.xmlhttp. 6.0',' msxml2.xmlhttp. 3.0',' msxml2.xmlhttp ']; // Lower versions of Ie may encounter three different versions of XMR objects var I, len; for(i = 0,len = versions.length; i < len ; i++){ try{ new ActiveXObject(version[i]); arguments.callee.activeXString = versions[i]; break; } the catch (ex) {/ / skip}}} return new ActiveXObject (the arguments. The callee. ActiveXString); }else{ throw new Error("No XHR object available."); } } var xhr = createXHR(); Xhr. open("get","path/to/example.txt",false); Xhr. send(null); // Get does not need to pass data. Obtain the data returned from the server if ((XHR. Status > = 200 && XHR. Status < 300) | | XHR. Status = = 304) {the console. The log (XHR. The responseText); }else{ console.log("Request was nsuccessful : " + xhr.status); }Copy the code

Cross-domain solutions

What is cross-domain? As long as the protocol, domain name, and port of the accessed resource are not identical, it can be said that the non-same-origin policy generates cross-domain, which is a narrow sense. In broad terms: One of the major limitations of implementing Ajax communication through XHR stems from cross-domain security policies; By default, an XHR object can only access resources in the same domain as the containing page [P582]. Note: Some text and code references from common cross-domain solutions at the front end (full)

CORS

Cross-origin Resource Sharing (CORS) defines how browsers and servers should communicate when they must access cross-resources. The basic idea behind this is to use custom HTTP headers to let the browser communicate with the server to determine whether a request or response should succeed or fail. It should be considered for complex cross-domain requests.

For common cross-domain requests, only access-Control-allow-origin is required on the server, and cookies are required on both the front and back ends.

1. Front-end Settings

1.) native ajax

function createCORSRequest(method,url){ // Compatible processing, ie8/9 needs to use window.xdomainRequest
	var xhr = new XMLHttpRequest();
	// Set whether cookies are included in the front end
	xhr.withCredentials = true;
	
	if("withCredentials" in xhr){ // The others use withCredentials
		xhr.open(method,url,true);
	}else if(typeofXDomainRequest ! ='undefined'){
		xhr = new XDomainRequest();
		xhr.open(method , url);
	}else{
		xhr = null;
	}
	
	return xhr;
}

/ / get request
var request = createCORSRequest("get"."http://www.somewhere-else.com/page/");
if(request){
	request.onload = function(){
		// Process request. ResponseText
	};
	request.send();
}

// Post request with cookie
var requestXhr = createCORSRequest("post"."http://www.somewhere-else.com/page/");
requestXhr.setRequestHeader("Content-Type"."application/x-www-form-urlencoded");
requestXhr.send("user=admin");
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) { alert(xhr.responseText); }};Copy the code

2.)jquery ajax

Using jquery ajax is more comfortable:

$.ajax({
	...
	xhrFields: {
		withCredentials: true // Set whether cookies are included in the front end
	},
	crossDomain: true.// The request header contains additional cross-domain information, but does not contain cookies. });Copy the code

3.) the vue framework

Add the following code to the Vue-Resource wrapped Ajax build:

Vue.http.options.credentials = true;
Copy the code

2. Set the server

If the backend Settings are successful, no cross-domain error message is displayed on the console of the front-end browser. If the backend Settings are unsuccessful, no cross-domain error message is displayed on the console.

1.) Java background

/ * * import packages: import javax.mail. Servlet. HTTP. HttpServletResponse; * Defined in interface parameters: HttpServletResponse Response */
response.setHeader("Access-Control-Allow-Origin"."http://www.domain1.com");  // Write full (protocol + domain + port)
response.setHeader("Access-Control-Allow-Credentials"."true");
Copy the code

2.) the node back

var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request'.function(req, res) {
    var postData = ' ';

    // Data block received
    req.addListener('data'.function(chunk) {
        postData += chunk;
    });

    // Data is received
    req.addListener('end'.function() {
        postData = qs.parse(postData);

        // Cross-domain background Settings
        res.writeHead(200, {
            'Access-Control-Allow-Credentials': 'true'.// The backend allows sending cookies
            'Access-Control-Allow-Origin': 'http://www.domain1.com'.// Allowed domain (protocol + domain name + port)
            'Set-Cookie': 'l=a123456; Path=/; Domain=www.domain2.com; HttpOnly'   // HttpOnly: The script cannot read cookies
        });

        res.write(JSON.stringify(postData));
        res.end();
    });
});

server.listen('8080');
console.log('Server is running at port 8080... ');
Copy the code

JSONP

JSONP, short for JSON with Padding, or parametric JSON, is a new way to apply JSON that has become very popular in web services. Simple cross-domain requests use JSONP.

Usually, in order to reduce the load of the Web server, we separate static resources such as JS, CSS and IMG to another server with an independent domain name, and then load static resources from different domain names in the HTML page through corresponding tags, which are allowed by the browser. Based on this principle, we can dynamically create script. Request a reference url to achieve cross-domain communication.

1. Front-end implementation

1.) Native implementation

<script>
	var script = document.createElement('script');
	script.type = 'text/javascript';
	
	// Pass the parameter and specify the callback execution function as onBack
	script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
	document.head.appendChild(script);
	
	// The callback executes the function
	function onBack(res){
		console.log(JSON.stringify(res));
	}
</script>
Copy the code

The server returns the following (executes the global function when it returns) :

onBack({"status": true."user":"admin"})
Copy the code

2.) the jquery ajax

$.ajax({
	url: 'http://www.domain2.com:8080/login'.type: 'get'.dataType: 'jsonp'.// The request is jSONP
	jsonpCallback: 'onBack'.// Custom callback function name
	data: {}
});
Copy the code

3.) the vue. Js

this.$http.jsonp('http://www.domain2.com:8080/login', {params: {},
	jsonp: 'onBack '
}).then((res) = >{
	console.log(res);
});
Copy the code

2. Example of back-end NodeJS code:

var qs = require('querystring');
var http = require('http');
var server = http.createServer();

server.on('request'.function(req,res){
	var params = qs.parse(req.url.split('? ') [1]);
	var fn = params.callback;
	
	// jsonp returns Settings
	res.writeHead(200, {"Content-Type":"text/javascript"});
	res.write(fn + '('+JSON.stringify(params)+') ');
	
	res.end();
});

server.listen('8080');
console.log('Server is running at port 8080 ... ');
Copy the code

⚠️ JSONp disadvantage: Only one get request can be implemented.

The WebSocket protocol is cross-domain

WebSocket Protocol is a new protocol for HTML5. It implements full duplex communication between browser and server while allowing cross-domain communication.

The native WebSocket API is not very convenient to use. In the example, socket. IO is used, which encapsulates the WebSocket interface well, provides a simpler and flexible interface, and provides backward compatibility for browsers that do not support WebSocket.

1. Front-end code

<div>User input:<input type="text"></div>
<script src="./socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080');

// The connection was successfully processed
socket.on('connect'.function() {
    // Listen for server messages
    socket.on('message'.function(msg) {
        console.log('data from server: ---> ' + msg); 
    });

    // The listener server is closed
    socket.on('disconnect'.function() { 
        console.log('Server socket has closed.'); 
    });
});

document.getElementsByTagName('input') [0].onblur = function() {
    socket.send(this.value);
};
</script>
Copy the code

2. The node socket background

var http = require('http');
var socket = require('socket.io');

// Start the HTTP service
var server = http.createServer(function(req, res) {
    res.writeHead(200, {
        'Content-type': 'text/html'
    });
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080... ');

// Listen for socket connections
socket.listen(server).on('connection'.function(client) {
    // Receive information
    client.on('message'.function(msg) {
        client.send('hello:' + msg);
        console.log('data from client: ---> ' + msg);
    });

    // Disconnect processing
    client.on('disconnect'.function() {
        console.log('Client socket has closed.'); 
    });
});
Copy the code

RequestAnimationFrame () frame animation

RequestAnimationFrame creates a smooth animation [P682]. RequestAnimationFrame is compared to setTimeout or setInterval before:

  • No time interval is required and the browser refresh frequency is appropriate
  • It stops running when it switches to another page

Examples of use are as follows:

<div id="num">1</div>
Copy the code
// Compatible with browsers
(function(){
    var lastTime = 0;
    var vendors = ['webkit'.'moz'.'ms'.'-o'];
    for(var x = 0; x <vendors.length && !window.requestAnimationFrame; ++x){
        window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x] + 'cancelAnimationFrame'] | |window[vendors[x] + 'CancelRequestAnimationFrame'];
    }
    if(!window.requestAnimationFrame){
        window.requestAnimationFrame = function(callback){
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0.16 - (currTime - lastTime));
            var id = window.setTimeout(function(){
                callback;
            },timeToCall);
            lastTime = currTime - timeToCall;
            returnid; }}if(!window.cancelAnimationFrame){
        window.cancelAnimationFrame = function (id){ clearTimeout(id); }}}) ();// Simple count
var num = 1,
	 timer;
fn();
function fn(){
	document.getElementById('num').innerText = ++num;
	timer = requestAnimationFrame(fn);
}
document.onclick = function(){
	cancelAnimationFrame(timer);
}
Copy the code

Please click here for the original link