1. Advanced functions

1-1 Type detection for security

When you think of type detection, the first thing that comes to mind is what types exist in the Javascript world (this is a very old question, really).

We can roughly divide them into two types: basic type and reference type. The basic type includes string, number, bool, undefined and NULL. The reference type includes Array, Function and ObjectCopy the code

So let’s use type and instanceof to look at what’s returned by each of these types of data. Why is it unsafe to use type and instanceof for type detection

const str = 'test'
const num = 12
const bool = false
const unde = undefined
const nulls = null
const Array = [1.2.3.4]
const Object = {name: 'zzz'}

const checkType = (type) = > {
	return typeof(type)
}

// Use type to determine
console.log(checkType(str))			// string
console.log(checkType(num))			// number
console.log(checkType(bool))		// boolean
console.log(checkType(unde))		// undefined
console.log(checkType(nulls))		// object
console.log(checkType(Array))		// object
console.log(checkType(Object))		// object
// It is clear that null, Array, and Object are not safe enough to return Object.

// Use instanceof to judge

const checkInstance = (type) = > {
	return type instanceof String
}

console.log(checkInstance(str))	// is false.

// We need to introduce the principle of Instanceof.

Copy the code

The 1-1-1 instanceof principle

Instanceof’s function implementation is whether the former is an instanceof the latter, the specific code is: eg:


let res = a instanceof A

// a is an instance of A
// A is the constructor of A
// if __proto__ == a prototype then a instanceof a == true otherwise = false
Copy the code

A few key points are as follows:

  • Explain constrcutor, Proto, prototype and prototype object.

  • Prototype/proto/constructor

  • Back up, A.proto refers to the prototype object of A

  • A. protoType refers to the prototype object of the instance object

var Foo = function() {
	this.setName = (name) = > {
		this.name = name
	}
}

var foo = newFoo Foo. Prototype points to => prototype object// Multiple objects instantiated by the same constructor have the same prototype object. Inheritance is often implemented using prototype objectsFoo. The prototype. The constructor to = > constructor itself (Foo) Foo __proto__ point = > prototype object (understand common object) as Foo constructor to = > constructor (Foo)Copy the code

1-2 scope-safe constructors

Calling the function constructor in the global scope causes redundant attributes to be added to the global scope because new is not used


function Person(name,job) {
	this.name = name
	this.job = job
}

// If New is used

var person = Person('zhangsan'.'sell') 

console.log(window.name, window.job)	// zhangsan sell
console.log(person.name, person.job)	// VM76:11 Uncaught TypeErrorr
Copy the code

The problem is caused by the late binding of this, so we need to verify that this is an instance of the correct type inside the function:

function Person(name){
    if(this instanceof Person){
	    this.name = 'zhang';
    } else {
        return new Person(name)
    }
}

var person = Person('zhang')
console.log(window.name)	/ /"
console.log(person.name)	// zhang
Copy the code

1-3 Lazy loading functions

Lazy loading the branch that represents the execution of the function is executed the first time the function is called. During the first call, the function is overwritten by another function that executed properly, so that any calls to the original function are not judged by the execution branch. (Saving calculation force)

1-3-1 Application Scenarios

1. AJAX compatibility in different browsers; 2. The same function and method of H5 embedded in APP are written differently in different environments; 3.

1-3-2 Points for attention

1, the more frequently the application, the more can reflect the advantages of this mode 2, fixed, a judgment, will not change in a fixed application environment 3, complex branch judgment, no difference, do not need to apply this mode

1-3-3 Demo


const getCurEnv = (a)= > {
	// The current environment is Chrome
	return window.navigator.userAgent.toLowerCase().match(/chrome/i)! = =null
}

const Person = function(name) {
	this.name = name
}

const http = {
	created: function() {
		if (getCurEnv()) {
			console.log(this)
			this.created = function() {
				console.log('test1')
				return new Person('zhang1')}console.log('test2')
		    return new Person('zhang2')}else {
			this.created = function() {
				console.log('test3')
				return new Person('zhang3')}}},Atest: (a)= > {
		console.log(this)    // window {}
	},
	Ftest: function() {
		console.log(this)    // http {}
	}
}

http.created()	// test2 Person {name: "zhang2"}
http.created()  // test1 Person {name: "zhang1"}

// The two methods on the lazy loading function actually return the same value. This is how lazy loading functions work.
Copy the code

1-4 Function bindings

This technique is often used with callback functions and event handling to preserve the code execution environment while passing functions as variables

Many JavaScript libraries implement a function that binds functions to a given environment, usually called bind(). A simple bind() function takes a function and an environment and returns a function that calls the given function in the given environment, passing all arguments intact. This function returns a closure.

The above language description is always unreal, let’s go straight to the Demo:

1-4-1 Demo


var obj1 = {
	name: 'zhang'.getName: function() {
		console.log(arguments[0] [2].'obj1')
		return this.name
	}
}

var obj2 = {
	name: 'lisi'.getName: function() {
		console.log(arguments.'obj2')
		return this.name
	}
}

function Bind(fn, context) {
	return fn.call(context, arguments)
}

Bind(obj1.getName,obj2,'xxxxx')	

ƒ : ƒ] "obj1"
// 'lisi'
// Arguments are different from arguments
// arguments.
Copy the code

1-4-2 arguments

Class Array (Array – like)

  • Each element can be accessed using subscripts
  • Has length attribute
  • Arguments’ data type is Object
  • You can use the for and for-in methods
  • No native Array methods available

Demo


var test = function() {
	console.log(arguments)
	console.log(arguments[0])
	console.log(arguments.length)
	console.log(typeof arguments)
	for(var i = 0; i<arguments.length; i++) {
		var ele = arguments[i]
		console.log(ele)
    }
	for(x in arguments) {
		console.log(arguments[x])
	}
	// arguments.split(' ')
	// Uncaught TypeError: arguments.split is not a function
}

test(1.2.3.4)
// Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
/ / 1
/ / 4
// object
// 1, 2, 3, 4
// 1, 2, 3, 4
Copy the code

Convert a class array to an array

  • Method one:
var test = function() {
	console.log(arguments)
	var arrArg = Array.prototype.slice.call(arguments)
	console.log(arrArg)
}

test(1.2.3.4) // [1, 2, 3, 4]
Copy the code
  • Method 2:
var test = function() {
	console.log(arguments)
	var arrArg = Array.from(arguments)
	console.log(arrArg)
}

test(1.2.3.4) // [1, 2, 3, 4]
Copy the code

1-4-3 ES5 native bind() method in detail

The text is still a bit difficult to explain, so we will continue to showCode ~ Demo:

var obj = {
	a: 1.b: 2.getCount: function(c, d) {
		return this.a + this.b + c + d
	}
}
console.log(obj.getCount(3.4))	/ / 10
window.a = window.b = 0
var funcs = obj.getCount
funcs(3.4)						/ / 7

Copy the code

Bind is a function extension of function, and the code rebinds this to func (obj).

var obj = {
	a: 1.b: 2.getCount: function(c, d) {
		return this.a + this.b + c + d
	}
}
console.log(obj.getCount(3.4))	/ / 10
window.a = window.b = 100
var funcs = obj.getCount.bind(obj)
funcs(3.4)			/ / 10
// var funcs = obj.getCount.bind(window)
/ / / / funcs (3, 4), 207
Copy the code

1-5 Currization of functions

Also known as partial evaluation. Corrification itself is fixing an expected parameter and returning a specific function to handle batch-specific requirements. This increases the applicability of the function, but also reduces its scope. The definition of words is always hard to accept, so let’s try showCode: Suppose you want to write an accounting tool, and then record the data for each day, and finally for the whole week. how ?

let weekCost = 0
const cost = function(num) {
	weekCost += num 
}
cost(100)	/ / 100
cost(200)	/ / 300
cost(300)	/ / 600
Copy the code
At this time, there will be a general ledger every day, which I do not want to see, because I do not want to be heartache by this general ledger every day, after all, the salary is not enough. I just want a ledger stimulation once a week.Copy the code
const currying = function(fn) {
    let args = []
	return function() {
	    if (arguments.length == 0) {
		    return fn.apply(this, args)
	    } else {
			let test = [].push.apply(args,arguments)
			// return fn.call(this, arguments)}}}const costs = (function() {
	let money = 0
	return function() {
		money = 0
		for(let i = 0; i<arguments.length; i++) {
			money += arguments[i]
		}
		return money
	}
})()

let cost = currying(costs)

cost(100)
cost(100)
cost(100)
cost(100)
cost(100)

console.log(cost())	/ / 500

cost(100)
cost(100)

console.log(cost())	/ / 700
Copy the code

A summary:

In the dMEO above, if cost() is called explicitly with arguments, it indicates that the actual evaluation is not being done, but the arguments are being saved, and the cost() function returns another function. It is only when we execute cost() without arguments that we actually begin the evaluation with all the arguments saved previously. This is a concrete granulation of functions. So how do we generalize if we want to abstract function granulation? 🤔 for example, let's take a look at granulation again!Copy the code

Demo

const currying = function(fn) {
	let args = Array.prototype.slice.call(arguments.1)
	return function() {
		let innerArgs = Array.prototype.slice.call(arguments)
		return fn.apply(this, args.concat(innerArgs))
	}
}

const add = function(n, m) {
	return n + m
}

var curriedAdd = currying(add, 3)

console.log(curriedAdd(5)) / / 8

Copy the code

Summary 2:

In this example, granulation is used to create a function that has one or more parameters set. And we're going to do more examples to prove this point.Copy the code

Pay attention to

Both currified and bound functions introduce additional overhead and should not be overused.

1-6 Currization of inverse functions

On the contrary, the effect of anti-Currization is to extend the applicability of functions, so that a function that is a function of a particular object can be used by any object.

Core:

Using uncurrying, a method that can only be used by one object can be extended to more objects that can reference it. ShowCode:


Function.prototype.uncurrying = function() {
    var that = this;
    return function() {
        return Function.prototype.call.apply(that, arguments); }}const test1 = {}
const test2 = {}
 
test1.sayHi = function () {
    return "Hello " + this.value +""+[].slice.call(arguments);
}

test2.sayHiuncurrying = test1.sayHi.uncurrying()

console.log(test2.sayHiuncurrying({value:'world'},"hahaha"));

// Hello world hahaha
Copy the code

The core code has been shown, read it carefully

Well, today, first write here, after the Demo will continue to improve the explanation, do not understand can leave a message to discuss ~

GitHub address: (Welcome star, welcome recommendation:)

Advanced Front-end Skills, Higher-order Functions (I)