Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Summary of advanced skills of JS

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly introduces "the summary of advanced skills of JS". In daily operation, I believe many people have doubts about the summary of advanced skills of JS. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful for you to answer the doubts of "summary of advanced skills of JS"! Next, please follow the editor to study!

1. Secure type detection

The problem is how to safely detect the type of a variable, such as determining whether a variable is an array. A common practice is to use instanceof, as shown in the following code:

Let data = [1,2,3]; console.log (data instanceof Array); / / true

But the above judgment will fail under certain conditions-when judging the variables of a parent window in iframe. Write a demo to verify the following main.html of the main page:

Window.global = {arrayData: [1,2,3]} console.log ("parent arrayData installof Array:" + (window.global.arrayData instanceof Array))

Determine the variable type of the parent window in iframe.html:

Console.log ("iframe window.parent.global.arrayData instanceof Array:" + (window.parent.global.arrayData instanceof Array))

In iframe, use window.parent to get the global window object of the parent window, which is no problem regardless of whether it is cross-domain or not, and then you can get the variables of the parent window, and then use instanceof to judge. The final running result is as follows:

You can see that the judgment of the parent window is correct, while the judgment of the child window is false, so a variable is obviously Array, but not Array. Why? Since this is a problem that occurs only in the parent-child window, try changing Array to the Array of the parent window, namely window.parent.Array, as shown in the following figure:

True is returned this time, and then other judgments are changed, such as the figure above. Finally, we can know that the root cause is the last judgment in the figure above:

Array! = = window.parent.Array

They are two functions, the parent window defines a, the child window defines another, the memory address is different, the memory address is different, the Object equation judgment is not valid, and window.parent.arrayData.constructor returns the Array of the parent window, the comparison is in the child window, using the Array of the child window, the two Array are not equal, so the judgment is not valid.

What are we going to do?

Since you cannot use Object's memory address judgment, you can use the string method, because the string is the basic type, and the string comparison is fine as long as each character is equal. ES5 provides such a method, Object.prototype.toString. Let's try the return values of different variables.

You can see that if the array returns "[object Array]", ES5 specifies this function as follows:

ES5 function address: https://262.ecma-international.org/5.1/#sec-15.2.4.2

That is, the return value of this function starts with "[object", followed by the name of the variable type and the closing parenthesis. So since it is a standard syntax specification, you can use this function to safely determine whether a variable is an array.

It can be written as follows:

Object.prototype.toString.call ([1,2,3]) = = "[object Array]"

Note that instead of calling call directly, the first argument to call is the context execution context, which you pass the array to as the execution context.

An interesting phenomenon is that the class of ES6 also returns function:

So you can know that class is also a prototype implemented in function, that is to say, class and function are essentially the same, but written differently.

Does that mean that instanceof can no longer be used to determine the type of variable? No, you have to use this method when you need to detect the variable type of the parent page, the variable on this page can still be determined by instanceof or constructor, as long as you can make sure that the variable does not cross the page. Because most people rarely know how to write iframe code, there is no need to do it in a more troublesome way, just a simple way.

two。 Lazy load function

Sometimes you need to do some compatibility judgment in the code, or do some UA judgment, as shown in the following code:

/ / UA type getUAType: function () {let ua = window.navigator.userAgent; if (ua.match (/ renren/i)) {return 0;} else if (ua.match (/ MicroMessenger/i)) {return 1;} else if (ua.match (/ weibo/i)) {return 2;} return-1;}

The function of this function is to determine in which environment the user opened the web page, so as to count which channel works better.

A feature of this type of judgment is that its result is dead, and no matter how many times the judgment is executed, it will return the same result, for example, the user's UA cannot be changed on this page (except for the debugging settings). So in order to optimize, there is a lazy function, and the above code can be changed to:

/ / UA type getUAType: function () {let ua = window.navigator.userAgent; if (ua.match (/ renren/i)) {pageData.getUAType = () = > 0; return 0;} else if (ua.match (/ MicroMessenger/i)) {pageData.getUAType = () = > 1; return 1 } else if (ua.match (/ weibo/i)) {pageData.getUAType = () = > 2; return 2;} return-1;}

After each judgment, the getUAType function is re-assigned to a new function, and the function directly returns a certain variable, so that each subsequent acquisition does not have to judge, this is the role of the lazy function. You might say how much time can be optimized by such a few judgments, which makes almost no difference to users. That's true, but as an aspiring programmer, he will still find ways to optimize his code as much as possible, rather than just to complete the requirements. And qualitative changes occur when your optimizations accumulate to a certain amount. When I was in college, my C++ teacher gave an example, saying that there was a system that was slow to ask her to have a look. One of the optimizations she made was to change the double precision of decimals to single precision, and in the end, it was a lot faster.

But in fact, we have a simpler implementation of the above example, that is, just save a variable:

Let ua = window.navigator.userAgent;let UAType = ua.match (/ renren/i)? 0: ua.match (/ MicroMessenger/i)? 1: ua.match (/ weibo/i)? 2:-1

Do not even write the function, the disadvantage is that even if the variable UAType is not used, a judgment will be performed, but we think that the probability of this variable being used is still very high.

Let's take another useful example. Since the trackless browsing of Safari forbids local storage, you need to make a compatibility judgment:

Untraceless browsing of Data.localStorageEnabled = true;// Safari disables localStoragetry {window.localStorage.trySetData = 1;} catch (e) {Data.localStorageEnabled = false;} setLocalData: function (key, value) {if (Data.localStorageEnabled) {window.localStorage [key] = value;} else {util.setCookie ("_ L _" + key, value, 1000);}}

When setting up local data, you need to determine whether local storage is supported, and if so, use localStorage, otherwise use cookie instead. You can modify it with an inert function:

SetLocalData: function (key, value) {if (Data.localStorageEnabled) {util.setLocalData = function (key, value) {return window.localStorage [key];}} else {util.setLocalData = function (key, value) {return util.getCookie ("_ L _" + key);}} return util.setLocalData (key, value);}

It is possible to reduce one if/else judgment here, but it does not seem to be particularly advantageous. After all, in order to reduce one judgment, a concept of inert function is introduced, so you may have to weigh whether this introduction is worth it. If there are three or five judgments, it should be better.

3. Function binding

Sometimes when one function is passed as an argument to another function, the execution context of the function often changes, as shown in the following code:

Class DrawTool {constructor () {this.points = [];} handleMouseClick (event) {this.points.push (event.latLng);} init () {$map.on ('click', this.handleMouseClick);}}

The this in the execution callback of the click event does not point to the instance of DrawTool, so the this.points in it will return undefined. The first solution is to use closures to cache the this and change it to that:

Class DrawTool {constructor () {this.points = [];} handleMouseClick (event) {this.points.push (event.latLng);} init () {let that = this; $map.on ('click', event = > that.handleMouseClick (event));}}

Since the callback function is executed with that, and that is a real example pointing to DrawTool, there is no problem. On the contrary, if there is no that, it uses this, so it depends on where this points.

Because we use the arrow function, and the this of the arrow function still points to the context of the parent, you don't have to create a closure yourself here, you can just use this:

Init () {$map.on ('click', event = > this.handleMouseClick (event));} it is easier to copy the code. The second way is to bind the code using ES5's bind function, as follows: init () {$map.on (' click', this.handleMouseClick.bind (this));}

This bind may seem magical, but you can actually implement a bind function with just one line of code:

Function.prototype.bind = function (context) {return () = > this.call (context);}

Just return a function whose this is the original function pointed to, and then let it call (context) bind the execution context.

4. Corey

Corialization is the combination of functions and parameter values to produce a new function, as shown in the following code, assuming that there is a curry function:

Function add (a, b) {return a + b;} let add1 = add.curry (1); console.log (add1 (5)); / / 6console.log (add1 (2)); / / 3

How to implement such a curry function? Its key point is to return a function that has some closure variables that record the default parameters at the time of creation, and then when the return function is executed, spell the newly passed parameters and default parameters into a complete parameter list to adjust the original function, so you have the following code:

Function.prototype.curry = function () {let defaultArgs = arguments; let that = this; return function () {return that.apply (this, defaultArgs.concat (arguments));}}

But because the parameter is not an array, there is no concat function, so you need to convert the pseudo array into a pseudo array, you can use Array.prototype.slice:

Function.prototype.curry = function () {let slice = Array.prototype.slice; let defaultArgs = slice.call (arguments); let that = this; return function () {return that.apply (this, defaultArgs.concat (slice.call (arguments);}}

Now let's take a useful example of Coriarization. When you need to sort an array in descending order, you need to write this:

Let data = [1, 5, 2, 3, 10]; data.sort ((a, b) = > b-a); / / [10, 5, 3, 2, 1]

Pass a function argument to sort, but if you have a lot of descending operations, it's a bit annoying to write a function parameter every time, so you can solidify this parameter with Corialization:

Array.prototype.sortDescending = Array.prototype.sort.curry ((a, b) = > b-a)

This makes it much more convenient:

Let data = [1, 5, 2, 2, 3, 10]; data.sortDescending (); console.log (data); / / [10,5,3,2,1] 5. Prevent tampering with objects

Sometimes you may be afraid that your partner has been changed by mistake, so you need to protect it.

(1) Object.seal prevents attributes from being added or deleted

In the following code, when an object is seal, attributes cannot be added or removed:

When strict mode is used, an exception is thrown:

(2) Object.freeze freezes objects

This cannot change the attribute value, as shown in the following figure:

At the same time, you can use Object.isFrozen, Object.isSealed, Object.isExtensible to judge the state of the current object.

(3) defineProperty freezes a single attribute

As shown in the following figure, if enumable/writable is set to false, this property will not be traverable and writable:

6. Timer

How to implement a JS version of the sleep function? Because there are sleep functions in languages such as C/C++/Java, but JS does not. The purpose of the sleep function is to put the thread into hibernation and then call it back up after the specified time. You can't write a while loop and keep judging whether the difference between the current time and the start time is at the specified time, because it takes up CPU and is not dormant.

This implementation is relatively simple, we can use setTimeout + callback:

Function sleep (millionSeconds, callback) {setTimeout (callback, millionSeconds);} / / sleep 2 seconds sleep (2000, () = > console.log ("sleep recover"))

But using callbacks makes it impossible for my code to be written down like a waterfall stream like normal code. I have to get a callback function to pass a value as an argument. So I thought of Promise, and now I rewrite it with Promise:

Function sleep (millionSeconds) {return new Promise (resolve = > setTimeout (resolve, millionSeconds));} sleep (2000). Then () = > console.log ("sleep recover"))

But there seems to be no way to solve the above problem, and you still need to pass a function argument.

Although using Promise is essentially the same, it has a resolve parameter that makes it easy to tell it when to end asynchronously, and then it can execute then, especially when callbacks are more complex. Promise is still more convenient.

ES7 has added two new attributes, async/await, to handle asynchronous cases, so that asynchronous code is written like synchronous code, as shown in the async version of sleep:

Function sleep (millionSeconds) {return new Promise (resolve = > setTimeout (resolve, millionSeconds));} async function init () {await sleep (2000); console.log ("sleep recover");} init ()

Compared to the simple Promise version, the implementation of sleep remains the same. However, add an await before the call to sleep, so that the following code will not be executed until the asynchronous sleep is completed. At the same time, you need to package the code logic in a function marked with async, which returns a Promise object. When all the asynchrony in it is finished, you can then:

Init () .then (() = > console.log ("init finished"))

The new properties of ES7 make our code more concise and elegant.

Another important topic about timers is the difference between setTimeout and setInterval. As shown in the following figure:

SetTimeout does not start timing until the current execution unit is finished, while setInterval starts timing immediately after setting the timer. You can use a practical example, which I mentioned in the article "JS and Multithreading", here to actually run it with code, as shown in the following code:

Let scriptBegin = Date.now (); fun1 (); fun2 (); / / work unit function act (functionName) {console.log (functionName, Date.now ()-scriptBegin) that needs to execute 20ms; let begin = Date.now (); while (Date.now ()-begin

< 20);}function fun1() { let fun3 = () =>

Act ("fun3"); setTimeout (fun3, 0); act ("fun1");} function fun2 () {act ("fun2-1"); var fun4 = () = > act ("fun4"); setInterval (fun4, 20); act ("fun2-2");}

The execution model of this code looks like this:

Console output:

It is consistent with the above model analysis.

Let's move on to the last topic, function throttling.

7. Function throttling throttling

The purpose of throttling is not to trigger execution too quickly, such as:

Monitoring input triggering search monitoring resize making responsive adjustment monitoring mousemove adjusting location

Let's first see how many times resize/mousemove event 1s can be triggered, so we write the following driver code:

Let begin = 0 if count = 0 if window. Onresize = function () {count++; let now = Date.now (); if (! begin) {begin = now; return;} if ((now-begin)% 3000

< 60) { console.log(now - begin, count / (now - begin) * 1000); }}; 当把窗口拉得比较快的时候,resize事件大概是1s触发40次: 需要注意的是,并不是说你拉得越快,触发得就越快。实际情况是,拉得越快触发得越慢,因为拉动的时候页面需要重绘,变化得越快,重绘的次数也就越多,所以导致触发得更少了。 mousemove事件在我的电脑的Chrome上1s大概触发60次: 如果你需要监听resize事件做DOM调整的话,这个调整比较费时,1s要调整40次,这样可能会响应不过来,并且不需要调整得这么频繁,所以要节流。 怎么实现一个节流呢,书里是这么实现的: function throttle(method, context) { clearTimeout(method.tId); method.tId = setTimeout(function() { method.call(context); }, 100);} 每次执行都要setTimeout一下,如果触发得很快就把上一次的setTimeout清掉重新setTimeout,这样就不会执行很快了。但是这样有个问题,就是这个回调函数可能永远不会执行,因为它一直在触发,一直在清掉tId,这样就有点尴尬,上面代码的本意应该是100ms内最多触发一次,而实际情况是可能永远不会执行。这种实现应该叫防抖,不是节流。 把上面的代码稍微改造一下: function throttle(method, context) { if (method.tId) { return; } method.tId = setTimeout(function() { method.call(context); method.tId = 0; }, 100);} 这个实现就是正确的,每100ms最多执行一次回调,原理是在setTimeout里面把tId给置成0,这样能让下一次的触发执行。实际实验一下: 大概每100ms就执行一次,这样就达到我们的目的。 但是这样有一个小问题,就是每次执行都是要延迟100ms,有时候用户可能就是最大化了窗口,只触发了一次resize事件,但是这次还是得延迟100ms才能执行,假设你的时间是500ms,那就得延迟半秒,因此这个实现不太理想。 需要优化,如下代码所示: function throttle(method, context) { // 如果是第一次触发,立刻执行 if (typeof method.tId === "undefined") { method.call(context); } if (method.tId) { return; } method.tId = setTimeout(function() { method.call(context); method.tId = 0; }, 100);} 先判断是否为第一次触发,如果是的话立刻执行。这样就解决了上面提到的问题,但是这个实现还是有问题,因为它只是全局的第一次,用户最大化之后,隔了一会又取消最大化了就又有延迟了,并且第一次触发会执行两次。那怎么办呢? 笔者想到了一个方法: function throttle(method, context) { if (!method.tId) { method.call(context); method.tId = 1; setTimeout(() =>

Method.tId = 0100);}}

Execute immediately each time the trigger is triggered, and then set a timer to set tId to 0. The actual effect is as follows:

This implementation is more concise than the previous implementation and can solve the problem of latency.

So by throttling, reduce the number of execution to 10 times per second, the throttling time can also be controlled, but lose sensitivity at the same time, if you need high sensitivity, you should not use throttling, such as a drag-and-drop application. What happens if you drag and cut costs? Users will find that one card is dragged together.

At this point, the study of "Advanced skills Summary of JS" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report