变量对象

这篇文章要给大家介绍的是变量对象。 在JavaScript中,我们肯定不可避免的需要声明变量和函数,可是JS解析器是如何找到这些变量的呢?我们还得对执行上下文有一个进一步的了解。 在知识点《执行上下文》中,我们已经知道,当调用一个函数时(激活),一个新的执行上下文就会被创建。而一个执行上下文的生命周期可以分为两个阶段。 创建阶段 在这个阶段中,执行上下文会分别创建变量对象,建立作用域链,以及确定this的指向。 代码执行阶段 创建完成之后,就会开始执行代码,这个时候,会完成变量赋值,函数引用,以及执行其他代码。 从这里我们就可以看出详细了解执行上下文极为重要,因为其中涉及到了变量对象,作用域链,this等很多人没有怎么弄明白,但是却极为重要的概念,它关系到我们能不能真正理解JavaScript。在后面的文章中我们会一一详细总结,这里我们先重点了解变量对象。 变量对象(Variable Object) 变量对象的创建,依次经历了以下几个过程。 建立arguments对象。检查当前上下文中的参数,建立该对象下的属性与属性值。 检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。 检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。 许多读者在阅读到这的时候会因为下面的这样场景对于“跳过”一词产生疑问。既然变量声明的foo遇到函数声明的foo会跳过,可是为什么最后foo的输出结果仍然是被覆盖了? function foo() { console.log('function foo') } var foo = 20; console.log(foo); // 20 其实只是大家在阅读的时候不够仔细,因为上面的三条规则仅仅适用于变量对象的创建过程。也就是执行上下文的创建过程。而foo = 20是在执行上下文的执行过程中运行的,输出结果自然会是20。对比下例。 console.log(foo); // function foo function foo() { console.log('function foo') } var foo = 20; // 上栗的执行顺序为 // 首先将所有函数声明放入变量对象中 function foo() { console.log('function foo') } // 其次将所有变量声明放入变量对象中,但是因为foo已经存在同名函数,因此此时会跳过undefined的赋值 // var foo = undefined; // 然后开始执行阶段代码的执行 console.log(foo); // function foo foo = 20; 根据这个规则,理解变量提升就变得十分简单了。在很多文章中虽然提到了变量提升,但是具体是怎么回事还真的很多人都说不出来,以后在面试中用变量对象的创建过程跟面试官解释变量提升,保证瞬间提升逼格。 在上面的规则中我们看出,function声明会比var声明优先级更高一点。为了帮助大家更好的理解变量对象,我们结合一些简单的例子来进行探讨。 // demo01 function test() { console.log(a); console.log(foo()); var a = 1; function foo() { return 2; } } test(); 在上例中,我们直接从test()的执行上下文开始理解。全局作用域中运行test()时,test()的执行上下文开始创建。为了便于理解,我们用如下的形式来表示 ...

November 6, 2024

background

引言 在日常前端开发中,经常需要进行背景或背景图的处理。但是大多数前端er并未真正清楚背景的正确使用方式。经过本章的学习,相信你一定可以解决99%的背景处理问题。 一:简单使用 背景颜色和背景图片是可以共同出现的 div { width: 300px; height: 300px; background-color: red; background-image: url(./imgs/1.jpg); background-repeat: no-repeat; } 页面展示 一:background-repeat background-repeat决定背景图片的平铺方式 属性值 background-repeat:repeat (默认值) background-repeat:no-repeat (不平铺) background-repeat:repeat-x (水平方向平铺) background-repeat:repeat-y (垂直方向平铺) 1. repeat 默认情况下,如果背景图片不能铺满整个盒子时,系统会在水平和垂直方向同时平铺直到覆盖整个盒子 div { width: 300px; height: 300px; background-color: red; background-image: url(./imgs/1.jpg); background-repeat: repeat; } 2.repeat-x 如果背景图片不能将盒子的水平方向铺满,则在水平方向采取平铺处理直到铺满盒子的水平方向。 div { width: 300px; height: 300px; background-color: red; background-image: url(./imgs/1.jpg); background-repeat: repeat-x; } 3.repeat-y 如果背景图片不能将盒子的垂直方向铺满,则在垂直方向采取平铺处理直到铺满盒子的垂直方向。 div { width: 300px; height: 300px; background-color: red; background-image: url(./imgs/1.jpg); background-repeat: repeat-y; } 二:background-position background-positiont决定背景图片在盒子区域的定位位置。其方位由水平和垂直决定 1.px设置 px决定了背景图片在盒子水平和垂直方向偏移指定px的距离。 div { width: 300px; height: 300px; background-color: red; background-image: url(./imgs/1.jpg); background-repeat: no-repeat; background-position: 100px 100px; } ...

October 10, 2024

methods

Methods 的初始化 在 Vue.js 中,每个组件实例(vm)都会有一个 methods 属性,这个属性包含了组件中定义的所有方法。这些方法需要被初始化到实例上,以便可以在模板和生命周期钩子中被调用。初始化过程如下: function initMethods(vm, methods) { for (var key in methods) { vm[key] = methods[key] == null ? noop : bind(methods[key], vm); } } 这段代码遍历 methods 对象中的每个属性,并将其复制到 Vue 实例(vm)上。如果方法不存在(methods[key] == null),则赋值为一个空函数(noop),以避免对 undefined 的调用。否则,使用 bind 方法将每个方法的 this 绑定到 Vue 实例上。 Methods 作用域的固定 在 JavaScript 中,函数的 this 值是在函数被调用时确定的,而不是在定义时。这可能导致在 Vue 实例的方法中 this 并不指向实例本身,尤其是在方法被传递给其他函数或作为回调函数时。Vue 使用 bind 来解决这个问题,确保 this 总是指向 Vue 实例。 原生和兼容性 bind 实现 Vue.js 提供了两种 bind 的实现:原生的和兼容性的。兼容性实现是为了兼容那些不支持 Function.prototype.bind 的旧浏览器。 function polyfillBind(fn, ctx) { function boundFn(a) { var l = arguments.length; return l ? (l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a)) : fn.call(ctx); } boundFn._length = fn.length; return boundFn; } function nativeBind(fn, ctx) { return fn.bind(ctx); } var bind = Function.prototype.bind ? nativeBind : polyfillBind; polyfillBind 函数创建了一个新的函数 boundFn,它在调用时使用 call 或 apply 将 fn 的 this 绑定到 ctx 上。 nativeBind 函数直接使用原生的 bind 方法。 bind 变量根据浏览器是否支持原生 bind 来选择使用哪种实现。 使用 bind 的好处 使用 bind 后,我们可以在组件的方法中直接使用 this 来访问实例的属性和方法,而不用担心 this 的指向问题。这使得代码更加简洁和安全。 ...

October 4, 2024

密码存储

我们在开发网站或者APP时,首先要解决的问题,就是如何安全地传输和存储用户的密码。一些大公司的用户数据库泄露事件也时有发生,带来非常大的负面影响。因此,如何安全传输存储用户密码,是每位程序员必备的基础。本文将跟大家一起学习,如何安全传输存储用户的密码。 1. 如何安全地传输用户的密码 要拒绝用户密码在网络上裸奔,我们很容易就想到使用https协议,那先来回顾下https相关知识吧~ 1.1 https 协议 「http的三大风险」 为什么要使用https协议呢?http它不香吗? 因为http是明文信息传输的。如果在茫茫的网络海洋,使用http协议,有以下三大风险: 窃听/嗅探风险:第三方可以截获通信数据。 数据篡改风险:第三方获取到通信数据后,会进行恶意修改。 身份伪造风险:第三方可以冒充他人身份参与通信。 如果传输不重要的信息还好,但是传输用户密码这些敏感信息,那可不得了。所以一般都要使用https协议传输用户密码信息。 「https 原理」 https原理是什么呢?为什么它能解决http的三大风险呢? https = http + SSL/TLS, SSL/TLS 是传输层加密协议,它提供内容加密、身份认证、数据完整性校验,以解决数据传输的安全性问题。 为了加深https原理的理解,我们一起复习一下 一次完整https的请求流程吧~ 客户端发起https请求 服务器必须要有一套数字证书,可以自己制作,也可以向权威机构申请。这套证书其实就是一对公私钥。 服务器将自己的数字证书(含有公钥、证书的颁发机构等)发送给客户端。 客户端收到服务器端的数字证书之后,会对其进行验证,主要验证公钥是否有效,比如颁发机构,过期时间等等。如果不通过,则弹出警告框。如果证书没问题,则生成一个密钥(对称加密算法的密钥,其实是一个随机值),并且用证书的公钥对这个随机值加密。 客户端会发起https中的第二个请求,将加密之后的客户端密钥(随机值)发送给服务器。 服务器接收到客户端发来的密钥之后,会用自己的私钥对其进行非对称解密,解密之后得到客户端密钥,然后用客户端密钥对返回数据进行对称加密,这样数据就变成了密文。 服务器将加密后的密文返回给客户端。 客户端收到服务器发返回的密文,用自己的密钥(客户端密钥)对其进行对称解密,得到服务器返回的数据。 「https一定安全吗?」 https的数据传输过程,数据都是密文的,那么,使用了https协议传输密码信息,一定是安全的吗?其实不然 比如,https 完全就是建立在证书可信的基础上的呢。但是如果遇到中间人伪造证书,一旦客户端通过验证,安全性顿时就没了哦!平时各种钓鱼不可描述的网站,很可能就是黑客在诱导用户安装它们的伪造证书! 通过伪造证书,https也是可能被抓包的哦。 1.2 对称加密算法 既然使用了https协议传输用户密码,还是 「不一定安全」,那么,我们就给用户密码 「加密再传输」 呗~ 加密算法有 「对称加密」 和 「非对称加密」 两大类。用哪种类型的加密算法 「靠谱」 呢? 对称加密:加密和解密使用 「相同密钥」 的加密算法。 常用的对称加密算法主要有以下几种哈: 如果使用对称加密算法,需要考虑 「密钥如何给到对方」 ,如果密钥还是网络传输给对方,传输过程,被中间人拿到的话,也是有风险的哦。 1.3 非对称加密算法 再考虑一下非对称加密算法呢? 「非对称加密:」 非对称加密算法需要两个密钥(公开密钥和私有密钥)。公钥与私钥是成对存在的,如果用公钥对数据进行加密,只有对应的私钥才能解密。 ...

October 2, 2024

展开运算符

ES6 新增了很多特性,扩展运算符就是其中一个。简单的三个点号,让原本繁琐的代码瞬间变得简洁、优雅。就让我们看看它有哪些常见用法吧。 让函数的 Apply 调用方式成为过去 有时候我们需要通过 Function.prototype.apply 来调用函数,参数就放在一个数组里。 function doStuff (x, y, z) { } var args = [0, 1, 2]; // 调用函数并传参 doStuff.apply(null, args); 现在有了展开运算符,我们可以完全避免使用 apply,只需在调用函数时在数组前加上展开运算符: doStuff(...args); 代码变得更短、更简洁,也不需要一个毫无用处的 null! 合并数组 合并数组有各种方式,但是展开运算符为我们提供了一种新思路: arr1.push(...arr2) // 在数组后面添加 arr2 里的元素 arr1.unshift(...arr2) //在数组前面添加 arr2 里的元素 如果你想合并两个数组,并且想把元素放到数组中的任何位置,你可以这么做: var arr1 = ['two', 'three']; var arr2 = ['one', ...arr1, 'four', 'five']; // ["one", "two", "three", "four", "five"] 代码少了很多,还可以随意控制位置! 拷贝数组 拷贝数组是个常见的任务,过去我们使用 Array.prototype.slice 来完成,但现在我们可以通过展开运算符得到数组的副本: var arr = [1,2,3]; var arr2 = [...arr]; // 等同于 arr.slice() arr2.push(4) 这里要注意,数组里的对象仍然是引用类型的,因此实际上并不是所有内容都被“拷贝”了。 将参数或 NodeList 转成数组 跟拷贝数组类似,我们用 Array.Prototype.slice 将 NodeList和 arguments 对象转成真正的数组,但是现在我们可以使用展开运算符完成这个任务: ...

October 2, 2024

表单控件

01、<form>表单 表单是比较重要的HTML元素,主要作用是向服务端提交数据。比如结合表单元素input使用,通过内部的button按钮提交(type=“submit”)表单数据。 元素/属性 描述 值/备注 <form> 表单元素 action 提交表单的目标(服务端)地址url method 提交数据的方式,就是数据传输的方式 get:通过URL提交数据 url?uname=1&age=2;post,通过HTTP表单数据提交,键值格式。 target 提交数据时打开action url的方式 _self:当前窗口(默认值);_blank:新窗口 enctype 编码类型(encode type),规定了form表单在发送到服务器时候编码方式,不常用。 application/x-www-form-urlencoded:编码所有字符(默认);multipart/form-data :混合类型, 表单中有文件上传时使用;text/plain:纯文体,空格转换为 “+” 加号,不对特殊字符编码 submit() 提交表单数据,通过js代码调用 <fieldset> 表单分组,默认样式:一个框 便于表单样式管理的语义化元素 form from的id,当<fieldset>不在form中时 disabled 整个分组都不可用 <legend> 作为<fieldset>的标题,显示在框上 (legend /ˈledʒənd/ 铭文、图例) <style> #form fieldset { border: 1px solid skyblue; padding: 20px 10px; border-radius: 5px; text-align: center; margin: 10px 0px; } #form fieldset legend { font-size: 1em; border: 1px solid rgb(236, 175, 43); border-radius: 1em; padding: 3px 15px; } </style> <form id="form" action="#" target="_self" method="post"> <fieldset> <legend>登录</legend> <input type="text" name="uname" placeholder="请输入用户名" required maxlength="36"> <input type="password" name="upwd" required maxlength="12" placeholder="输入密码"> <input type="submit" value="submit-登录"> </fieldset> </form> 📢注意:<form>提交数据时参数名为表单元素的name,因此表单控件须设置name属性。 ...

September 25, 2024

版本控制

什么是版本控制 版本控制是一种系统性的方法,用于管理和跟踪软件开发项目中的代码、文件和资源。它的核心目标是在不同时间点的开发中保持代码的一致性、可追溯性和可维护性。 为什么要做版本控制 这个问题可以从定义中的关键词可以看出端倪:不同时间点下的代码一致性、可追溯性、可维护性。这里额外补充一点:当今的互联网开发早已步入敏捷迭代,各种动态化方案层出不穷都有着顺应时代潮流的意思,因此软件开发一定会越来越多地涉及到团队开发,团队之间的协作也一定需要这样一个机制去保障稳定性。 代码一致性 版本控制系统允许开发人员有效地管理和组织项目中的代码。它跟踪每个文件的历史记录,记录了每个版本的变更,包括谁做了什么修改、何时以及为什么。 可追溯性 版本控制系统允许为重要的里程碑或发布创建标签(tag),以便轻松识别和回滚到特定版本。 并且当某个版本的代码出现问题,版本控制系统允许开发人员轻松地回滚到之前的稳定版本,并在不破坏其余代码的情况下进行修复。 可维护性 开发人员可以创建分支(branch),这是一个独立的代码副本,用于开发新功能、修复错误或进行实验性工作。然后,这些分支可以与主代码库合并,以实现功能集成。 团队协作 在多人协作的环境中,不同开发者可能同时修改代码。版本控制系统确保不同的修改不会互相冲突,以及如何解决冲突。 版本控制系统促进了团队之间的协作,可以通过代码审查工具进行代码审查,以确保代码质量和一致性。 版本控制工具Git Git的安装 WIndows 访问 Git官网,选择对应的操作系统下载即可。 这里下载独立安装包即可 点install就行,会自动安装一个git GUI工具,但通常不会直接用它 下载完成安装即可,输入以下命令验证安装是否成功 git version 图形化工具 安装 这里推荐TortoiseGit这个工具,非常方便。但我觉得Git本身学习成本并不高,GUI工具更多是为了提升效率,基本的Git知识点还是需要掌握的。 下载本体 然后是语言包 安装时一路next即可,安装完本体后再安装语言包。 配置好Github的账户、邮箱、秘钥等,注意配置好了密钥也不会在下述部分显示,可以打开编辑全局进行查看 使用 在使用之前先提前补充一个点(后续会详细介绍),Git有本地仓库和远端仓库的概念,通常支持HTTPS和SSH两种协议进行关联,两种方式优缺点也十分明显: HTTPS:无需额外配置,但是在每次连接远端仓库时需要输入账号密码进行校验。 SSH:生成一组密钥对,需要本地进行配置,好处是配置好后每次连接远端仓库时会自动比较密钥对,无需额外校验。关于SSH详细可以参考这篇文章ssh-远程登录协议 为了一劳永逸,我们这里配置下SSH,首先打开Git bash # 输入下列命令,最后一个参数是您的邮箱 $ ssh-keygen -t rsa -C yourEmail.com 可以看到密钥对已经生成了 进入上述目录,注意.ssh文件夹默认是隐藏的,直接访问路径就行 以Github为例,配置公钥 创建一个Demo项目,平时学习的话就创建公有项目就行 复制一下我们的项目链接 选取一个你喜欢的目录作为本地工作区,然后按鼠标右键 ...

August 7, 2025

跨域相关

一、什么是跨域? 1.什么是同源策略及其限制内容? 同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。 同源策略限制内容有: Cookie、LocalStorage、IndexedDB 等存储性内容 DOM 节点 AJAX 请求发送后,结果被浏览器拦截了 但是有三个标签是允许跨域加载资源: <img src=XXX> <link href=XXX> <script src=XXX> 2.常见跨域场景 当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。常见跨域场景如下图所示: 特别说明两点: 第一:如果是协议和端口造成的跨域问题“前台”是无能为力的。 第二:在跨域问题上,仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议, 域名和端口必须匹配”。 这里你或许有个疑问:请求跨域了,那么请求到底发出去没有? 跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。 二、跨域解决方案 1.jsonp 1) JSONP原理 利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。 2) JSONP和AJAX对比 JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求) 3) JSONP优缺点 JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。 4) JSONP的实现流程 声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。 创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是show('我不爱你')。 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。 在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP函数。 // index.html function jsonp({ url, params, callback }) { return new Promise((resolve, reject) => { let script = document.createElement('script') window[callback] = function(data) { resolve(data) document.body.removeChild(script) } params = { ...params, callback } // wd=b&callback=show let arrs = [] for (let key in params) { arrs.push(`${key}=${params[key]}`) } script.src = `${url}?${arrs.join('&')}` document.body.appendChild(script) }) } jsonp({ url: 'http://localhost:3000/say', params: { wd: 'Iloveyou' }, callback: 'show' }).then(data => { console.log(data) }) 上面这段代码相当于向http://localhost:3000/say?wd=Iloveyou&callback=show这个地址请求数据,然后后台返回show('我不爱你'),最后会运行show()这个函数,打印出’我不爱你' ...

July 28, 2025

条件渲染的常见方法和注意事项

条件渲染方式 1. if 语句 先从 React 最基本的条件类型来看。如果有数据就显示组件,如果没有数据就不显示任何内容。posts 为需要渲染的列表: export default function App() { const { posts } = usePosts(); if (!posts) return null; return ( <div> <PostList posts={posts} /> </div> ); } 这种形式会生效的原因就是我们会提前返回,如果满足条件(posts 值不存在),就通过return null 在组件中不显示任何内容。 如果有多个要检查的条件时,也可以使用 if 语句。例如,在显示数据之前检查加载和错误状态: export default function App() { const { isLoading, isError, posts } = usePosts(); if (isLoading) return <div>Loading...</div>; if (isError) return <div>Error!</div>; return ( <div> <PostList posts={posts} /> </div> ); } 这里我们可以多次使用 if 语句,不需要再使用 else 或者 if-eles 语句,这样就减少了需要编写的代码,并且可读性更强。 2. 三元运算符 当我们想提前退出或者什么都不显示时,if 语句会很有用。但是,如果我们不想写一个与返回的 JSX 分开的条件,而是直接在其中写呢?那就可以使用三元表达式来编写条件。 在 React 中,我们必须在 JSX 中包含表达式,而不是语句。这就是为什么我们在 JSX 中只能使用三元表达式,而不是 if 语句来编写条件。 ...

December 17, 2024

meta 标签

概览 meta标签一般放在整个html页面的head部分,在MDN中对他这样定义: meta是文档级元数据元素,用来表示那些不能由其它 HTML 元相关元素(<base>、<link>, <script>、<style>或 <title>)之一表示的任何元数据。 是不是感觉看起来很抽象?说白了就是为了传达信息。 先看看meta 元素定义的元数据的类型: 如果设置了 name属性,meta 元素提供的是文档级别的元数据,应用于整个页面。 如果设置了 http-equiv属性,meta 元素则是编译指令,提供的信息与类似命名的 HTTP 头部相同。 如果设置了 charset属性,meta 元素是一个字符集声明,告诉文档使用哪种字符编码。 如果设置了 itemprop 属性,meta 元素提供用户定义的元数据。 name属性 name和content一起使用,前者表示要表示的元数据的名称,后者是元数据的值。 author 用来表示网页的作者的名字,例如某个组织或者机构。 <meta name="author" content="aaa@mail.abc.com"> description 是一段简短而精确的、对页面内容的描述。以头条和taobao的description标签为例: keywords 与页面内容相关的关键词,使用逗号分隔。某些搜索引擎在遇到这些关键字时,会用这些关键字对文档进行分类。 还是以头条和taobao为例 viewpoint 为 viewport(视口)的初始大小提供指示。目前仅用于移动设备。 可能你也发现了,我们在vscode中自动生成html的代码片段时,会自动生成: <meta name="viewport" content="width=device-width, initial-scale=1.0"> width用来设置 viewport 的宽度为设备宽度; initial-scale为设备宽度与 viewport 大小之间的缩放比例。 robots 表示爬虫对此页面的处理行为,或者说,应当遵守的规则,是用来做搜索引擎抓取的。 它的content可以为: all:搜索引擎将索引此网页,并继续通过此网页的链接索引文件将被检索 none:搜索引擎讲忽略此网页 index:搜索引擎索引此网页 follow:搜索引擎继续通过此网页的链接索引搜索其它的网页 renderer 用来指定双核浏览器的渲染方式,比如360浏览器,我们可以通过这个设置来指定360浏览器的渲染方式 <meta name="renderer" content="webkit"> //默认webkit内核 <meta name="renderer" content="ie-comp"> //默认IE兼容模式 <meta name="renderer" content="ie-stand"> //默认IE标准模式 http-equiv http-equiv也是和content一起使用,前者表示要表示的元数据的名称,后者是元数据的值。 http-equiv 所有允许的值都是特定 HTTP 头部的名称, ...

November 5, 2024