这16种原生函数和属性的区别,你真的知道吗? 精心收集,高级前端必备知识,快快打包带走

本文涉及的产品
云解析DNS,个人版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 原生内置了很多API, 作用类似,却也有差千差万别,了解其区别,掌握前端基础,是修炼上层,成为前端高级工程师的必备知识,让我们一起来分类归纳,一起成长吧。

1.JPG


前言


原生内置了很多API, 作用类似,却也有差千差万别,了解其区别,掌握前端基础,是修炼上层,成为前端高级工程师的必备知识,让我们一起来分类归纳,一起成长吧。

上一篇前端基础好文: 那些你熟悉而又陌生的函数


属性获取 keys, getOwnPropertyNames, getOwnPropertySymbols


Object.keys


返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。


Object.getOwnPropertyNames


返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。


Object.getOwnPropertySymbols


一个给定对象自身的所有 Symbol 属性的数组。


Reflect.ownKeys


返回一个由目标对象自身的属性键组成的数组。

等同于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))

例子


const symbolSalary = Symbol.for("salary");
const symbolIsAnimal = Symbol.for("isAnimal");
const symbolSay = Symbol.for("say");
function Person(age, name){
   this.age = age;
   this.name = name;
   this.walk = function () {
       console.log("person:walk");
   }
}
// 原型方法
Person.prototype.say = function(words){
   console.log("say:", words);
}
Person.prototype[symbolSay] = function(words){
   console.log("symbolSay", words);
}
// 原型属性
Person.prototype[symbolIsAnimal] = true;
Person.prototype.isAnimal = true;
const person = new Person(100, "程序员");
person[symbolSalary] = 6000;
person["sex"] = "男";
// sex 不可枚举
Object.defineProperty(person, "sex", {
   enumerable: false
});
Object.defineProperty(person, symbolSalary, {
   enumerable: false, // 无效的设置 
   value: 999
});
const keys = Object.keys(person);
const names = Object.getOwnPropertyNames(person);
const symbols = Object.getOwnPropertySymbols(person);
const ownKeys = Reflect.ownKeys(person);
console.log("keys", keys);  // [ 'age', 'name', 'walk' ]
console.log("getOwnPropertyNames", names); // [ 'age', 'name', 'walk', 'sex' ]
console.log("getOwnPropertySymbols", symbolSalary); // [ Symbol(salary) ]
console.log("ownKeys", ownKeys); // [ 'age', 'name', 'walk', 'sex', Symbol(salary) ]
console.log("--------")
console.log(person.isAnimal);  // true
console.log(person[symbolIsAnimal]); // true
console.log(person[symbolSalary]);  // 999
person[symbolSay]("hello world"); // symbolSay hello world
person.say("hello world"); // say: hello world
person.walk(); // person:walk
复制代码


总结


  1. Object.keys:则返回的是所有可枚举属性键,也就是属性下的enumerable: true。但不包括Symbol值作为名称的属性键。
  2. Object.getOwnPropertyNames:返回的是对象所有自己的属性键 ,包括不可枚举属性但不包括Symbol值作为名称的属性键。
  3. Object.getOwnPropertySymbols: 方法返回一个给定对象自身的所有 Symbol 属性键的数组。
  4. Reflect.ownKeys: 返回一个由目标对象自身的属性键组成的数组。等同于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))。


节点位置关系 Node.contains, Node.compareDocumentPosition


Node.compareDocumentPosition


比较当前节点与任意文档中的另一个节点的位置关系

语法 compareMask = node.compareDocumentPosition( otherNode )

返回值是一个具有以下值的位掩码:


常量名 十进制值 含义
DOCUMENT_POSITION_DISCONNECTED 1 不在同一文档中
DOCUMENT_POSITION_PRECEDING 2 otherNode在node之前
DOCUMENT_POSITION_FOLLOWING 4 otherNode在node之后
DOCUMENT_POSITION_CONTAINS 8 otherNode包含node
DOCUMENT_POSITION_CONTAINED_BY 16 otherNode被node包含
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC 32 待定


在一些场景下,可能设置了不止一位比特值。比如 otherNode 在文档中是靠前的且包含了 Node, 那么DOCUMENT_POSITION_CONTAINS 和 DOCUMENT_POSITION_PRECEDING 位都会设置,所以结果会是 0x0A 即十进制下的 10。


看代码:

结果是: 20


  1. child 在 parent之后,赋值得4
  2. child 被 parent包含,赋值的16

4 + 16 = 20


<div id="parent">
        <div id="child"></div>
    </div>
    <script>
        const pEl = document.getElementById("parent");
        const cEl = document.getElementById("child");
        console.log(pEl.compareDocumentPosition(cEl));  // 20
    </script>
复制代码


Node.contains


返回的是一个布尔值,来表示传入的节点是否为该节点的后代节点

基本等于compareDocumentPosition的 | DOCUMENT_POSITION_CONTAINED_BY | 16 |otherNode被node包含 |


总结


  1. compareDocumentPosition 返回的是数字,带组合意义的数据,不仅仅可以返回包含,还可以返回在之前之后等信息
  2. contains 返回的是布尔值,仅仅告诉你是否有包含关系


取文本 innerText, textContent


HTMLElement.innerText


解析过程:


  1. 对HTML标签进行解析;
  2. 对CSS样式进行带限制的解析和渲染;
  3. 将ASCII实体转换为对应的字符;
  4. 剔除格式信息(如\t、\r、\n等),将多个连续的空格合并为一个


Node.textContent


解析过程:


  1. 对HTML标签进行剔除
  2. 将ASCII实体转换为相应的字符。


需要注意的是:


  1. 对HTML标签是剔除不是解析,也不会出现CSS解析和渲染的处理,因此<br/>等元素是不生效的。
  2. 不会剔除格式信息和合并连续的空格,因此\t、\r、\n和连续的空格将生效


例子


<p id="source">
        <style>
            #source {
                color: red;
            }
        </style>
        Take a look at<br>how this text<br>is interpreted
        below.
        <span style="display:none">HIDDEN TEXT</span>
    </p>
     <h3>Result of textContent:</h3>
    <textarea id="textContentOutput" rows="12" cols="50" readonly>...</textarea>
    <h3>Result of innerText:</h3>
    <textarea id="innerTextOutput" rows="12" cols="50" readonly>...</textarea>
    <script>
        const source = document.getElementById('source');
        const textContentOutput = document.getElementById('textContentOutput');
        const innerTextOutput = document.getElementById('innerTextOutput');
        textContentOutput.innerHTML = source.textContent;
        innerTextOutput.innerHTML = source.innerText;
    </script>
复制代码


看看结果:


2.JPG


总结


  1. innerText是会解析css的,<br/>有效,剔除格式信息(如\t、\r、\n等),将多个连续的空格合并为一个。
  2. textContent是剔除html标签,<br/>无效,\t、\r、\n和连续的空格将生效。


节点取值 value , nodeValue



Node.nodeValue


  • 对于text, comment, 和 CDATA 节点来说, nodeValue返回该节点的文本内容.
  • 对于 attribute 节点来说, 返回该属性的属性值.


对应着下面表格的 nodeType的值 text 3,4,8


常量 nodeType 值 描述
Node.ELEMENT_NODE 1 一个 元素 节点,例如

Node.TEXT_NODE 3 Element 或者 Attr 中实际的 文字
Node.CDATA_SECTION_NODE 4 一个 CDATASection,例如 <!CDATA[[ … ]]>。
Node.PROCESSING_INSTRUCTION_NODE 7 一个用于XML文档的 ProcessingInstruction (en-US) ,例如 声明。
Node.COMMENT_NODE 8 一个 Comment 节点。
Node.DOCUMENT_NODE 9 一个 Document 节点。
Node.DOCUMENT_TYPE_NODE 10 描述文档类型的 DocumentType 节点。例如 就是用于 HTML5 的。
Node.DOCUMENT_FRAGMENT_NODE 11 一个 DocumentFragment 节点


value


特定的一些HTMLElement元素,用value属性获取其值。常见的有value属性的元素如下:



<object classid="clsid:F08DF954-8592-11D1-B16A-00C0F0283628" id="Slider1" width="100" height="50">    
     <param name="BorderStyle" value="1" />
</object>
复制代码


总结


  1. nodeValue 是文本节点,属性节点,注释节点等类型的节点用来取值的方法
  2. vlaue是特定的元素节点用来取值的方法


节点复制 adoptNode, importNode, cloneNode



Document.adoptNode


将外部文档的一个节点拷贝一份,然后可以把这个拷贝的节点插入到当前文档中.


Document.importNode


从其他的document文档中获取一个节点。 该节点以及它的子树上的所有节点都会从原文档删除 , 并且它的ownerDocument 属性会变成当前的document文档。 之后你可以把这个节点插入到当前文档中。


Node.cloneNode


生成节点的一个副本。

实际上 Document 是继承自 Node, 也具备cloneNode方法。

3.JPG


这里提一个问题:


Document.adoptNodeDocument.importNode是操作外部文档的,那么操作所在的文档会有什么效果呢?


Node.cloneNode 有一个boolean类型的可选参数deep

  • true: 则该节点的所有后代节点也都会被克隆
  • false: 则只克隆该节点本身.


注意


  1. cloneNode deep参数在不同版本的浏览器实现中,默认值可能不一样, 所以强烈建议写上值。
  2. cloneNode 会克隆一个元素节点会拷贝它所有的属性以及属性值,当然也就包括了属性上绑定的事件(比如onclick="alert(1)"),但不会拷贝那些使用addEventListener()方法或者node.onclick = fn这种用JavaScript动态绑定的事件


总结


  1. adoptNode 从外部文档进行拷贝
  2. importNode 从外部文档进行拷贝,并从外部文档删除
  3. cloneNode 从本文档进行复制,有浅复制和深复制


父节点 childNodes , children


Node.childNodes


节点的子节点集合,包括元素节点、文本节点还有属性节点


ParentNode.children


返回的只是节点的元素节点集合, 即 nodeType为1的节点。


例子


来实际看一段代码:


<div id="root">
        1
        <span>2</span>
        3
        <!-- <div></div> -->
        <!CDATA[[ 4 ]]>
    </div>
    <script>
        const rootEl = document.getElementById("root");
        console.log(rootEl.children);
        console.log(rootEl.childNodes);
    </script>
复制代码


返回结果截图:

4.JPG


Node.parentNodeNode.parentElement也是同样的道理。

总结


  1. children只返回元素节点,也就是 nodeType为1的节点
  2. childNodes 返回所有类型的节点


添加节点 append, appendChild


node.appendChild


将一个节点附加到指定父节点的子节点列表的末尾处


ParentNode.append


方法在 ParentNode的最后一个子节点之后插入一组 Node 对象或 DOMString 对象。 被插入的 DOMString 对象等价为 Text 节点.


例子


我们一次append三个节点,其中两个文本节点,一个div节点。


<div id="root"></div>
  <script>
    function createEl(type, innerHTML){
        const el = document.createElement(type);
        el.innerHTML = innerHTML;
        return el;
    }
    const rootEl = document.getElementById("root");
    rootEl.append("我们", createEl("div", "都是"), "好孩子");
  </script>
复制代码

5.JPG


总结


  • ParentNode.append()允许追加 DOMString 对象,而 Node.appendChild() 只接受 Node 对象。
  • ParentNode.append() 没有返回值,而 Node.appendChild() 返回追加的 Node 对象。
  • ParentNode.append() 可以追加多个节点和字符串,而 Node.appendChild() 只能追加一个节点。


简直说, append强大太多了。


文档可见状态 Document.hidden, Document.visibilityState


document.hidden


返回布尔值,表示页面是(true)否(false)隐藏。


Document.visibilityState


返回document的可见性, 由此可以知道当前文档(即为页面)是在背后, 或是不可见的隐藏的标签页,或者(正在)预渲染.可用的值如下:


  • 'visible' : 此时页面内容至少是部分可见. 即此页面在前景标签页中,并且窗口没有最小化.
  • 'hidden' : 此时页面对用户不可见. 即文档处于背景标签页或者窗口处于最小化状态,或者操作系统正处于 '锁屏状态' .
  • 'prerender' : 页面此时正在渲染中, 因此是不可见的 . 文档只能从此状态开始,永远不能从其他值变为此状态 .注意: 浏览器支持是可选的.


当此属性的值改变时, 会递交 visibilitychange 事件给Document.


例子


我们先输出当前状态,然后点击别的tab,等下再点击回来。


console.log(
        "visibilityState:",
        document.visibilityState,
        " hidden:",
        document.hidden,
      );
      console.log("");
      document.addEventListener("visibilitychange", function () {
        console.log(
          "visibilityState:",
          document.visibilityState,
          " hidden:",
          document.hidden
        );
      });
复制代码

6.JPG


我们日常可以用visibilitychange来监听当前页面处于隐藏时,去清除定时器或页面中的动画, 停止音乐视频等的播放。


我还想到一个有意思的


  1. 广告倒计时

你离开后,不算倒计时,会不会被骂死

  1. 阅读某些协议

你离开后,停止倒计时


总结


  1. hidden 与 visibilityState 返回值不同,一个是布尔值,一个是字符串
  2. visibilityState 的状态多一种 prerender, 其对应的hidden的值是true
  3. visibilityState e有相关的事件


函数调用 call, apply, bind


Function.prototype.call


使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数


Function.prototype.apply


调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数


Function.prototype.bind


方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用


例子


function sum(...args) {
    const total = args.reduce((s, cur) => {
        return s + cur;
    }, 0);
    return (this.base || 0) + total;
}
const context = {
    base: 1000
};
const bindFun = sum.bind(context, 1, 2);
const callResult = sum.call(context, 1, 2, 3, 4);
const applyResult = sum.apply(context, [1, 2, 3, 4]);
const bindResult = bindFun(3, 4);
console.log("call:", callResult);  // 1010
console.log("apply:", applyResult); // 1010 
console.log("bind:", bindResult); // 1010
复制代码


总结


相同点,都能改变被调用函数的this指向。


  1. call: 第二个参数开始,可以接收任意个参数
  2. apply: 第二个参数,必须是数组或者类数组
  3. bind: 第二个参数开始,可以接收任意个参数 , 返回的是一个新的函数


注意点:


  1. bind调用多次,this指向第一次第一个参数

log 调用了两次bind, 第一次bind{ val: 1 }, 第二次bind{ val: 2 }, 输出的this是一次bind的上下文


function log() {
    console.log("this", this);
}
console.log(log.bind({ val: 1 }).bind({ val: 2 })())  // { val: 1 }
复制代码


再看一段类似的代码:


虽然this的指向不会再变改变,但是参数还是继续接受, arguments 长度为2, 第一次bind的1,第二次bind的 2 , 都照单全收。


function log() {
    console.log("this", this);   // { val: 1 }
    console.log("arguments", arguments);  // { '0': 1, '1': 2 }
}
console.log(log.bind({ val: 1 }, 1).bind({ val: 2 }, 2)())  // 1
复制代码


字符串截取 substr, substring


String.prototype.substr


返回一个字符串中从指定位置开始到指定字符数的字符

语法: 第二参数,是需要截取的长度


str.substr(start[, length])


String.prototype.substring


返回一个字符串在开始索引到结束索引之间的一个子集, 或从开始索引直到字符串的末尾的一个子集。

语法: 第二参数,结束索引

str.substring(indexStart[, indexEnd])


例子


提示:

  1. 两个参数都没设置的时候,效果相同
  2. 第一个参数是大于等于0的整数,没设置第二参数的时候,效果相同


const str = "我们都是好孩子";
console.log(str.substr())  // 我们都是好孩子
console.log(str.substring()) // 我们都是好孩子
console.log(str.substr(1))  // 们都是好孩子
console.log(str.substring(1)) // 们都是好孩子
console.log(str.substr(-1))  // 子
console.log(str.substring(-1)) // 我们都是好孩子
console.log(str.substr(1, 2))  // 们都
console.log(str.substring(1, 2))  // 们
复制代码


总结


  1. substr 第二个参数是需要截取的长度
  2. substring 第二个参数是结束索引值的
  3. 没指定参数或者第一个参数是大于等于0的整数时,效果相同
  4. 第一个参数是负数或者第二个参数是负数,处理规则不通


具体参见 substrsubstring


遍历 for of, for in


for in


获取enumerable:true的属性键


for of


遍历属性值。不受到enumerable限制。


例子


  1. 在数组原型上增加了方法gogo, for in结果中出现了,而 for of结果冲未出现。
  2. 定义了 属性2不能被遍历, for in结果中未出现,而 for of结果中出现了。


// 原型上增加方法
Array.prototype.gogo = function(){
    console.log("gogo");
}
var a = [1,2,3];
// key值2不可以枚举
Object.defineProperty(a, 2, {
    enumerable: false
});
Object.defineProperty(a, "2", {
    enumerable: false
});
for(let p in a){
    // 索引被遍历出来是字符串类型
    console.log(p, typeof p); // 0 string; 1 string; gogo string
}
console.log("---")
for(let v of a){
    console.log(v);  // 1 2 3
}
复制代码


总结


for in


  1. 获取enumerable:true的属性键。
  2. 可以遍历对象。
  3. 可以获取原型上的属性键。
  4. 数字属性键被遍历出来是字符串。 比如索引值


for of:


  1. 遍历属性值。不受到enumerable限制。
  2. 可遍历数组。 一般不可以遍历对象,如果实现了Symbol.iterator,可以遍历。 如Array,Map,Set,String,TypedArray,arguments 对象等等
  3. 不能获取原型上的值


当前时间 Date.now(), Performance.now()


Date.now()


方法返回自 1970 年 1 月 1 日 00:00:00 (UTC) 到当前时间的毫秒数。


Performance.now


获取当前的时间戳的值(自创建上下文以来经过的时间),其值是一个精确到毫秒的 DOMHighResTimeStamp.


<!DOCTYPE html>
<html lang="en">
<head>
    <script>
        console.log("p1", performance.now())
    </script>
</head>
<body>
    <script>
        console.log("p2", performance.now());
        setTimeout(() => {
            console.log("p3", performance.now());
        }, 1000)
    </script>
</body>
</html>
复制代码

7.JPG


总结


  1. Date.now()的基准是 1970 年 1 月 1 日 00:00:00 (UTC), 而Performance.now是上下文创建。
  2. Date.now()返回的是整数,Performance.now返回的是double类型
  3. 理论上Performance.now精度更高


域名信息 host, hostname


location.host


其包含:主机名,如果 URL 的端口号是非空的,还会跟上一个 ':' ,最后是 URL 的端口号


location.hostname


返回域名


例子


https与http的默认端口号,是不会被 host包含的,看下面的代码

https://developer.mozilla.org:443 的host是 developer.mozilla.org, 因为443是https的默认端口。


var anchor = document.createElement("a");
      anchor.href = "https://developer.mozilla.org:443/en-US/Location.host";      
      console.log(anchor.host == "developer.mozilla.org:443") // false
      console.log(anchor.host == "developer.mozilla.org") // true
      console.log(anchor.hostname == "developer.mozilla.org:443"); // false
      console.log(anchor.hostname == "developer.mozilla.org");  // true
      anchor.href = "https://developer.mozilla.org:4097/en-US/Location.host";
      console.log(anchor.host == "developer.mozilla.org:4097")  // true
      console.log(anchor.hostname == "developer.mozilla.org")  // true
复制代码


总结


  1. 默认端口下, host等于hostname
  2. host额外包含端口号


事件注册 on, addEventListener


内联事件


注册事件。


EventTarget.addEventListener


方法将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。


例子


分别注册两次onclick和click事件,onclick只输出一次,click输出两次.


<button id="btn" >点我</button>
    <script>
        const btnEl = document.getElementById("btn");
        btnEl.onclick = () => console.log("onclick", 1);
        btnEl.onclick = () => console.log("onclick", 1);
        btnEl.addEventListener("click", ()=> console.log("click", 1));
        btnEl.addEventListener("click", ()=> console.log("click", 2));
    </script>
复制代码

1.JPG


总结


  1. 内联事件是覆盖型,只能使用事件冒泡,addEventListener支持多个事件处理程序,并支持事件捕获。
  2. 内联事件特定情况下可以被Node.cloneNode复制,addEventListener的不行
    更多细节参见 Node.cloneNode
  3. addEventListener为DOM2级事件绑定,onclick为DOM0级事件绑定


按键时间 keypress, keydown


keypress


当按下产生字符值的键时触发按键事件。产生字符值的键的示例有字母键、数字键和标点键。不产生字符值的键的例子是修改键,如 Alt、 Shift、 Ctrl 或 Meta。

不再推荐使用此功能。尽管一些浏览器可能仍然支持它,但它可能已经从相关的 web 标准中删除


keydown


与keypress事件不同,无论是否生成字符值,所有键都会触发 keydown 事件。


例子


输入123,keydown和keypress的值keyCode一样


8.JPG


总结


  1. 触发顺序keydown -> keypress
  2. keydown:当用户按下键盘上的任意键时触发;
  3. keypress:当用户按下键盘上的字符键时触发; 对中文输入法支持不好,无法响应中文输入
  4. keypress的keyCode与keydown不是很一致;


异步加载脚本 defer,async


defer


异步加载,按照加载顺序执行脚本的


async


异步加载,乱序执行脚本。

这个一图胜千文


9.JPG


例子


四个script标签,两个async,两个defer。


代码内容如下:


  • async1: console.log("async1");
  • async2: console.log("async2");
  • defer1: console.log("defer1");
  • defer2: console.log("defer2");


<script src="./async1.js" async ></script>
    <div>
        sdfsdfsdfsdfsdfsdfd
    </div>
    <script src="./async2.js" async ></script>
    <script src="./defer1.js" defer ></script>
    <script src="./defer2.js" defer ></script>
复制代码

10.JPG


从上面可以看出,有时候 async2会比async1输出早,defer的输出也可能比async的输出早。 但是defer的输出一定 defer1然后defer2


总结


  1. 都是异步加载,defer会按照加载顺序执行,async乱序执行


相关文章
|
1天前
|
前端开发 应用服务中间件 nginx
网页设计,若依项目修改(It must be done)01----若依打包位置,nginx代理前端静态资源和后端接口,就是怎样设置转载,访问固定端口,让他访问其他资料的配置文件,访问/,给你那些
网页设计,若依项目修改(It must be done)01----若依打包位置,nginx代理前端静态资源和后端接口,就是怎样设置转载,访问固定端口,让他访问其他资料的配置文件,访问/,给你那些
|
2天前
|
JavaScript 前端开发 CDN
前端 JS 经典:package.json 属性详解
前端 JS 经典:package.json 属性详解
7 1
|
17天前
|
XML 存储 前端开发
【前端】XML和HTML的区别详解
【前端】XML和HTML的区别详解
20 5
|
25天前
|
存储 缓存 资源调度
你真的知道 NPM、Yarn 与 PNPM 这三个前端包管理器之间的区别吗?
【6月更文挑战第9天】NPM、Yarn和PNPM是主流前端包管理器,各有特色。NPM生态庞大,易用但速度慢;Yarn速度快,依赖管理稳定;PNPM性能优异,节省磁盘空间。Yarn和PNPM在速度和确定性上胜出,NPM因广泛使用和丰富资源领先。开发者可根据项目需求和喜好选择,三者共同推动前端开发进步。
31 8
|
22天前
|
缓存 前端开发 JavaScript
Webpack作为模块打包器,为前端项目提供了高度灵活和可配置的构建流程
【6月更文挑战第12天】本文探讨了优化TypeScript与Webpack构建性能的策略。理解Webpack的解析、构建和生成阶段是关键。优化包括:调整tsconfig.json(如关闭不必要的类型检查)和webpack.config.js选项,启用Webpack缓存,实现增量构建,代码拆分和懒加载。这些方法能提升构建速度,提高开发效率。
35 3
|
2天前
|
前端开发 JavaScript
前端 JS 经典:箭头函数的意义
前端 JS 经典:箭头函数的意义
4 0
|
2天前
|
前端开发 JavaScript
前端 JS 经典:函数签名
前端 JS 经典:函数签名
5 0
|
2天前
|
前端开发
前端 CSS 经典:mix-blend-mode 属性
前端 CSS 经典:mix-blend-mode 属性
4 0
|
2天前
|
前端开发
前端 CSS 经典:backface-visibility 属性
前端 CSS 经典:backface-visibility 属性
5 0
|
2天前
|
前端开发 JavaScript 开发者
前端 JS 经典:通用性函数封装思路
前端 JS 经典:通用性函数封装思路
11 0