js代码小问题大杂烩
window与document对象
在查阅资料时,发现两个非常好的图,觉得足以描述清楚两者的关系,如下:
JavaScript是通过访问BOM(Browser Object Model)对象来访问、控制、修改客户端(浏览器),由于BOM的window包含了document,window对象的属性和方法是直接可以使用而且被感知的,因此可以直接使用window对象的document属性,通过document属性就可以访问、检索、修改XHTML文档内容与结构。
因为document对象又是DOM的根节点。可以说,BOM包含了DOM(对象),浏览器提供出来给予访问的是BOM对象,从BOM对象再访问到DOM对象,从而js可以操作浏览器以及浏览器读取到的文档。
对象属性访问及引号问题
- 对象属性名可以加引号也可以不加引号,
- json属性名要加双引号
- 访问对象属性时,[“属性名”]或.属性名,最好使用[“属性名”],可防止报错
匿名函数立即执行
(function(){}).call(this);
与(function(_this){})(this);
都具有立即执行匿名函数的效用,且都将可将this指向window,避免use strict
模式下this指向undefined
变量前面放置单个"+"号
可以理解为Number(value)
会将其按照Number函数的规则转换为数值或者NaN,规则大概如下:
1. Boolean:true返回1,false返回0
2. 数据值,直接返回
3. null,返回0
4. undefined,返回NaN
5. 对于字符串,将其转换为十进制数值,会忽略前面的0(16进制除外),空字符串返回0,浮点数会返回浮点数值。其他格式字符串(无论是否数字开头,返回NaN,字符串中好几个小数点,返回NaN)
typeof console !== ‘undefined’
主要目的是兼容低版本IE,因为低版本原生ie(8及以下)window对象没有console,所以直接使用的话会导致脚本中断。完整代码示例如下:
if (typeof console !== "undefined" && console !== null) {
console.error(e.stack);
}
JQuery对象与js对象
jQuery对象其实是一个伪数组,这个数组对象包含125个方法和4个属性
4个属性:
- jquery 当前的jquery框架版本号
- length 指示该数组对象的元素个数
- context 一般情况下都是指向HtmlDocument对象
- selector 传递进来的选择器内容
下面就是在chrome浏览器控制台打印的一个jquery对象,0属性为该jquery对象包装的DOM对象,上述的125个方法以及jquery属性放在_proto_对象中
m.fn.init(1)
0: div#output
context: document
length: 1
selector: "#output"
__proto__: Object(0)
jQuery对象和js对象区别:
- jQuery对象属于js的数组;
- jQuery对象是通过jQuery包装的DOM对象后产生的;
- jQuery对象不能使用DOM对象的方法和属性
- DOM对象不能使用jQuery对象的方法和属性
jQuery对象和js对象之间的相互转换:
- js转jQuery对象:
$(js对象)
- jQuery对象转js对象:
var x = $("#output")[0];
- 使用jQuery对象本身提供的get函数来返回指定集合位置的DOM对象
var x = $("#output").get(0);
JavaScript match()方法和正则表达式match()
一、普通String.match()
先介绍参数为普通字符串的使用方式,此时match方法的返回值是存放首次匹配内容的数组。如果没有找到匹配结果,返回null。
- 语法结构:
str.match(searchvalue)
- 参数解析:searchvalu,必需,规定方法要检索的字符串
- 代码实例:
let str="antzone";
onsole.log(str.match("n"));
- 代码运行效果:
["n", index: 1, input: "antzone", groups: undefined]
对上述代码运行结果分析如下:
- match方法在有匹配结果的时候返回值是一个数组。
- 数组第一个元素是match方法首次匹配到的子字符串,“antzone"虽然有多个"n”,但是返回的数组只存储首次匹配到的"n",如果match方法的参数是全局匹配的正则,将会存储所有的匹配到的子字符串。
- index属性值返回首次匹配到子字符串的位置。
- input属性值是原字符串"antzone"。
- groups属性当前并不被支持,暂时不做介绍。
let str="antzone";
console.log(str.match("zon"));
代码运行效果:
["zon", index: 3, input: "antzone", groups: undefined]
首次匹配到子字符的位置,以子字符串的首字符在原字符串位置为准。
字符串与数组类似,字符位置从0开始计算,也就是第一个字符的位置为0,第二个位置为1,以此类推。
let str="antzone";
console.log(str.match("蚂蚁"));
代码运行效果:null
如果匹配不到任何内容,方法返回值为null
。
二、RegExp的match()
match方法可在字符串内查找一个或多个与指定正则表达式匹配的子字符串。
方法的返回值是存放着匹配结果的数组,但数组元素并不一定全都是匹配结果,后面会详细介绍。
- 语法结构:stringObject.match(reg)
- 参数解析:1. stringObject:从中查找子字符串的源字符串。
2. reg:正则表达式对象或者正则表达式直接量。非全局匹配:
所谓非全局匹配,也就是使用g修饰符此时,match方法最多只在字符串中成功匹配一次:
- 如果不存在匹配的子字符串,返回null。
- 如果存在匹配的子字符串,返回一个数组。
返回的数组解析如下:
- 第0个元素是匹配结果。
- 第1个元素是第一个引用型分组匹配的子字符串。
- 第2个元素存放的是第二个引用型分组匹配的子字符串,依次类推。
返回的数组对象同时具有两个属性:
- index:返回匹配的子字符串起始字符在stringObject源字符串中的位置。
- input:返回stringObject源字符串。
全局匹配:
match方法执行全局匹配,获取所有可以匹配的子字符串:
- match方法执行全局匹配,获取所有可以匹配的子字符串:
- 如果存在匹配的子字符串,返回一个数组。
与非全局匹配的区别:
- 不具有index和input属性。
- 不具有引用型分组匹配的子字符串,数组元素仅包含匹配的所有子字符串。
代码实例如下:
var str="本站url地址是www.softwhy.com"; var reg=/why/; console.log(str.match(reg));
上面代码采用非全局模式,运行效果如下:
["why", index: 16, input: "本站url地址是www.softwhy.com", groups: undefined]
当前JavaScript并不支持groups,先不用去管,分析如下:
- 数组第0个元素是匹配的子字符串。
- index属性值是"why"中"w"在源字符串中的位置。
- input属性值是整个源字符串。
var str="本站url地址是www.softwhy.com"; var reg=/w(h)y/; console.log(str.match(reg));
代码运行效果如下:
["why", "h", index: 16, input: "本站url地址是www.softwhy.com", groups: undefined]
分析如下:
- 数组第0个元素是匹配的子字符串。
- 数组第一个元素是正则表达式中引用分组所匹配的内容。
- index属性值是"why"中"w"在源字符串中的位置。
- input属性值是整个源字符串。
void 0, #, javascript:void(0),javascript:;
什么是void?
void是一元运算符,出现在操作数的左边,操作数可以是任意类型的值,该操作符指定要计算或运行一个表达式,返回值永远是undefined
,void右边的表达式可以是带括号形式(例如:void(0)
),也可以是不带括号的形式(例如:void 0
)。
在一些框架源码中,经常用void 0代替undefined,为什么要这样写呢?
- 使用void 0比使用undefined能够减少3个字节
- undefined并不是javascript中的保留字,我们可以使用undefined作为变量名字,然后给它赋值。void 0输出唯一的结果undefined,保证了不变性。
#与 javascript:void(0)的区别:
- #可以跳转到设置了id的目的地,点击链接后,页面会向上滚到页首,# 默认锚点为 #TOP
- javascript:void(0)则停留在原地,一动不动,我们称之为“死链接”
如下面这个例子:
<a href="javascript:void(0);">你点吧,反正我就是不动,嘿嘿嘿!!!</a><br>
<a href="#destination">点我跳转</a>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<h2 id="destination">目的地</h2>
- 当点击第一个链接时,一动不动
- 当点击第二个连接时,会跳转到指定id得位置(页面最底部)
javascript:;
javascript:是表示在触发超链接默认动作时,执行一段JavaScript代码,而 javascript:;表示什么都不执行,这样点击超链接时就没有任何反应。
json对象的浅拷贝与深拷贝
- 浅拷贝:
对于一个引用类型,如果直接将它赋值给另一个对象,只是将引用地址赋值给新对象,修改一个对象另一个也会被更改。
当需要复制后的对象独立于原对象,就要用深拷贝。 - 深拷贝:
深拷贝不是将原对象的引用赋值给新对象,而是新建一个对象,引用地址与原对象引用地址不同,再将原对象属性拷贝到新对象中。有两种常用方式实现引用类型的深拷贝,但都只适用于特定情况- Object.assign()方法。Object.assign()方法实现只实现了一层属性的深拷贝,属性值如果是对象,该对象并不会被深拷贝。
- Json.parse(Json.stringify(obj))方式。该方法成功实现了嵌套属性的深拷贝,但是原对象中的assign()方法丢失了。这是因为JSON.stringify()方法将对象转化为字符串,但只会处理简单属性和简单属性数组,constructor属性丢失了。因此,除非对象只有简单属性,没有constructor信息,才能使用Json.parse(Json.stringify(obj))做深拷贝。
- 自定义完全深拷贝方法:递归遍历对象所有属性。
js,css的引入路径后面加入参数的含义
<script type=”text/javascript” src=”jb51.js?version=1.2.6″></script>
<link rel='stylesheet' href='base.css?version=2.3.3′ type='text/css' />
类似上面的情况,使用参数有两种可能,
- 脚本并不存在,而是服务端动态生成的,因此带了个版本号,以示区别
- 客户端会缓存这些css或js文件,因此每次升级了js或css文件后,改变版本号,客户端浏览器就会重新下载新的js或css文件 ,刷性缓存的作用。大家可能有时候发现修改了样式或者js,刷新的时候不变,就是客户端缓存了css或者js文件,因此加上参数还是有好处的!
第二种情况最多,也可能两种同时存在。
(top,parent,self,window)区别及location详解
window对象
BOM的核心对象是window,它表示浏览器的一个实例。在浏览器中,window对象有双重角色:
- 通过javascript访问浏览器窗口的一个接口
- ECMAScript规定的Global对象
location对象
location对象既是window对象的属性,又是document对象的属性,换句话说,window.location和document.location引用的是同一个对象(只有在窗口内存在iframe时不一样)。location的href属性返回当前加载页面的完整url。
- window.location.href(当前URL)
http://www.myurl.com:8866/test?id=123&username=xxx
- window.location.protocol(协议)
http:
- window.location.host(域名 + 端口)
www.myurl.com:8866
- window.location.hostname(域名)
www.myurl.com
- window.location.port(端口)
8866
- window.location.pathname(路径部分)
/test
- window.location.search(请求的参数)
?id=123&username=xxx
框架(iframe/frameset)
如果页面中包含框架,则每个框架都拥有自己的window对象,window对象中又包含top、parent、self属性,可以通过window.parent、window.top等形式访问,那这三个属性之间有什么联系呢?
- top对象:始终指向最高(最外)层的框架,也就是浏览器窗口。对于在一个框架中编写的任何代码来说,其中windows对象指向的都是那个框架的特定实例,而非最高层框架;
- parent对象:始终指向当前框架的直接上层框架。parent不一定等于top,但在没有框架的情况下,parent=top;
- self对象:它始终指向当前框架window,可以和window的对象互换使用。
那么问题来了,对于浏览器窗口(最外层框架)或没有框架嵌套的页面呢?
是的,你没猜错,此时:top=parent=self=window
top.location.href = "www.xxx.com"
—— 在浏览器窗口打开该链接
parent.location.href = "www.xxx.com"
—— 在当前框架的直接上层框架(父框架)打开该链接
self.location.href = "www.xxx.com"
—— 在当前框架打开该链接
window.location.href = "www.xxx.com"
—— 在当前框架打开该链接
应用
禁止frame引用
if(window.top !==window.self) {
//若当前window对象和浏览器窗口对象不一致,则使用当前框架的url替换掉浏览器窗口的url
window.top.location.href=window.self.location.href;
//或者: top.location.href=self.location.href;
//或者: top.location.href=location.href;
}
用不用var及var,const,let区别
用不用var:
- 用var 定义过的变量,是不能被删除的,是全局变量。var 声明的变量存在提升,只有变量声明才会提升,对变量赋值并不会提升
- 没有被var定义的变量,是window对象的一个属性,是可以被删除的。在函数内部不用var声明的变量,也是window上的一个属性,可以删除。
- 当我们在封装函数的时候,在函数内部定义变量,用var定义,作用于仅在函数内部,不用var定义的变量,在函数外部还是可以取到的。
var,const,let:
ECMAScript是一个国际通过的标准化脚本语言。JavaScript由ECMAScript和DOM、BOM三者组成。可以简单理解为:ECMAScript是JavaScript的语言规范,JavaScript是ECMAScript的实现和扩展。ES5还只有全局作用域和函数作用域,ES6新增块级作用域。块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。
- var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。var 变量可以重复声明,如果重复使用的一个声明有一个初始值,那么它担当的不过是一个赋值语句的角色,如果重复使用的一个声明没有一个初始值,那么它不会对原来存在的变量有任何的影响
- let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。let 声明的变量不存在变量提升,换一种说法,就是 let 声明存在暂时性死区。在同一个块级作用域,不能重复声明变量。
- const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。const仅保证指针不发生改变,修改对象的属性不会改变对象的指针,所以是被允许的。
- 同一个变量只能使用一种方式声明,不然会报错
关于var变量提升和重复申明的一个示例:
var a = 100;
function fn() {
alert(a); //undefined
var a = 200;
alert(a); //200
}
fn();
alert(a); //100
var a;
alert(a); //100
var a = 300;
alert(a); //300
Array的一些方法
some()方法
arr.some(fn[, thisArg])
参数fn
是用来测试每个元素的函数,接受三个参数:
item
:数组中正在处理的元素。index
:数组中正在处理的元素的索引值。array
:some()被调用的数组。
参数thisArg
:执行 callback 时使用的 this 值。
some为数组中的每一个元素执行一次fn函数,直到找到一个使得fn返回 true 。如果找到了这样一个值,some将立即返回 ture ,其余的就不在运行,相当于短路运算符( && || )一样。如果到结束也没有找到,就会返回 false 。
some被调用不会改变数组。
例子:检测在数组中是否有大于10的元素:
var a = [11,50,40,3,5,80,90,4]
function fn(item,index,array){
console.log(item);
return item>10
}
a.some(fn);
//11
//true
当然这样还可以检索数组中是否存在某个值:
function some(t,i,a){
if(t == 10){
console.log (t+"存在于这个数组");
}
console.log (10+"不存在于这个数组");
}
var a = [11,50,40,3,5,80,90,4];
a.some(some)
// 10不存在于这个数组
// false
filter()方法
filter用于把Array的某些元素过滤掉,然后返回剩下的元素。
和map()类似,Array的filter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。
回调函数:
filter()接收的回调函数,其实可以有多个参数。通常仅使用第一个参数,表示Array的某个元素。回调函数还可以接收另外两个参数,表示元素的位置和数组本身:
var arr = ['A','B','C','d'];
var arr_filter = arr.filter(function(element,index,self){
console.log(element);/* 依次打印'A','B','C','d' */
console.log(index);/* 依次打印0,1,2,3 */
console.log(self);/* 打印数组本身即arr */
return true;
})
利用filter,可以巧妙地去除Array的重复元素:
var arr_repeat = ['A','B','A','B','B','C','A','D'];
var arr_filter = arr_repeat.filter(function(element,index,self){
return self.indexOf(element) == index;
})
console.log(arr_filter);//返回['A','B','C','D']
去除重复元素依靠的是indexOf总是返回第一个元素的位置,后续的重复元素位置与indexOf返回的位置不相等,因此被filter滤掉了。
示例1:在一个Array中,删掉奇数,只保留偶数,可以这么写:
<script type="text/javascript">
var arr = [0,1,2,3,4,5,1,4,0];
var arr_filter = arr.filter(function(x){
return x%2 == 0;/* 筛选偶数 */
})
console.log(arr_filter)
</script>
示例2:筛选去除空格
<script type="text/javascript">
var arr = ['0',1,2,3,4,"",5,1,4,'0',""];
var arr_filter = arr.filter(function(x){
return x;/* 筛选空格 */
})
console.log(arr_filter)
</script>