一.函数表达式
定义函数有两种形式:
- 函数声明
aa();
function aa() {
console.log(hello word);
};
这个例子不会报错,因为哎代码执行之前会先读取函数声明。关于函数声明最重要的一个特征就是函数声明提升。
- 函数表达式(匿名函数,拉姆达函数)
var aa = function (){
console.log(hello word);
};
1)递归
递归函数是在一个函数通过名字调用自身的情况下构成的。
function factorial(num){
if(num <= 1){
return 1;
}else {
return num * factorial(num - 1);
}
}
var ano = factorial;
factorial = null;
console.log(ano(4)); // error;
// ano被执行,factorial已经不是函数了就会报错。
// 方法1
function factorial(num){
if(num <= 1){
return 1;
}else {
return num * arguments.callee(num - 1);
}
}
// 方法2
var factorial = (function f(num){
if(num <= 1){
return 1;
}else {
return num * f(num - 1);
}
})();
2)闭包
闭包是指有权访问另一个函数作用域中的变量的函数。
1.闭包与变量
作用域链的这种机制引出一个副作用: 闭包只能取得包含函数中任何变量中的最后一个。
2. 关于this
在全局函数中,this等于window,而当函数被当作为某个对象的方法调用的时候。this等于那个对象。
但是在匿名函数的执行具有全局性,因此this通常指向window。但是在对象内部通常指向这个对象。
3.内存泄漏
内部函数被外部函数包含时,内部函数会将外部函数的局部活动对象添加到自己的作用域链中。而由于内部匿名函数的作用域链 在引用 外部包含函数的活动对象,即使outFun执行完毕了,它的活动对象还是不会被销毁!即,outerFun的执行环境作用域链都销毁了,它的活动对象还在内存中留着呢。并且根据垃圾回收机制,被另一个作用域引用的变量不会被回收。
闭包在执行过程中,活动对象会初始化包含此函数的活动对象和全局对象。但是函数在执行完之后,匿名函数的作用域链还在引用这个活动对象。(函数没有被销毁)所以要最使用完函数后要解绑函数引用。
3)模仿块级作用域
js是没有块级作用域的概念的。在这样的情况下闭包可以声明一个块级作用域。
(function(){
// 这里是块级作用域
var name = "小明";
})();
// 这样的写法可以减少内存占用。函数执行完毕或立即销毁。
4)私有变量
严格的来讲JavaScript中是每一偶私有成员的概念的;所有对象属性都共有的。但是有一个私有变量的概念。
1.静态只有变量
既然有私有变量那也就会有私有方法和公有(特权)方法。
(function(){
// 私有变量
var num = 10;
// 私有函数
function numFun() {
return false;
};
function MyObj() {};
// 公有/特权方法
MyObj.prototype.publiceMethod = function() {
num ++;
return numFun();
}
})();
2.模块模式
上面定义的是一种自定义类型的模式。还有一种是叫单例模式。所谓单例,指的就是只有一个实例的对象。
var singleton = function() {
// 私有变量
var num = 10;
// 私有函数
function numFun() {
return false;
};
// 公有/特权方法和属性
return {
publice: true,
publiceMethod: function(){
num++;
return numFun();
}
}
};
3.增强的模块模式
有人改进了模块模式:在返回对象之前加入其增强代码。
function CustomType() {};
var singleton = function () {
// 私有变量
var num = 10;
// 私有函数
function numFun() {
return false;
};
// 实例化方法/创建对象
var obj = new CustomType();
// 公有/特权方法和属性
obj.publice = true;
obj.publiceMethod = function () {
num++;
return numFun();
};
// 返回这个对象
return obj;
}();
console.log(singleton.publice);
function BaseComponents(){};
var application = function () {
// 私有变量和函数
var components = new Array();
// 初始化
components.push(new BaseComponents());
// 创建application的一个局部副本
var app = new BaseComponents();
app.getComponentCount = function() {
return components.length;
};
// 公共函数
app.registerComponent = function(component) {
if(typeof component === "object") {
components.push(component);
console.log(components);
}else{
console.error('对象类型异常')
}
};
// 返回对象
return app;
}();
var bb = {name: 1};
console.log(application.getComponentCount());
application.registerComponent(bb);
二.BOM
在web中使用javascript的话,那么核心就是BOM(浏览器对象模型)。
1)window 对象
BOM的核心对象是window,它表示浏览器的一个实例。相当于ECMAScript中的Global对象。
- 全局作用域
题外话:用var关键字定义的变量,会自动归在window对象下。如果window对象下有相同的属性,会被覆盖。但是ES6中的const和let不会。
var语句添加的window属性有一个名为[[Configurable]]的特性,通常这个值为false。所以这样定义出来的属性不能为delete删除。
- 窗口操作
// 获取窗口相对于屏幕的左边的距离
var leftPos = (typeof window.screenLeft === "number") ? window.screenLeft : window.screenX;
// 获取窗口相对于屏幕的上边的距离
var topPos = (typeof window.screenTop === "number") ? window.screenTop : window.screenY;
// 浏览器显示视图的宽度
var pageWidth = window.innerWidth;
// 浏览器显示视图的高度
var pageHeight = window.innerHeight;
if(typeof pageWidth !== "number"){
// 是否是标准模式
if(document.compatMode === "CCS1Compat"){
// 现代浏览器
pageWidth = document.documentElement.clientWidth;
pageHeight = document.documentElement.clientHeight;
}else{
// 浏览器兼容
pageWidth = document.body.clientWidth;
pageHeight = document.body.clientHeight;
}
}
// 浏览器本身的宽度
var outerWidth = window.outerWidth;
// 浏览器本身的高度
var outerHeight = window.outerHeight;
// 导航到一个特定的url
window.open("https://www.baidu.com/","测试","height=400","width=400","top=10","left=10","resizable=yse");
- 间接调用和超时调用
// 间接调用和清除
setTimeout(function(){
// 执行代码
},300)
clearTimeout()
// 超时调用和清除
setInterval(function(){
// 执行代码
},300)
clearInterval()
javaScript是一个单线程的解释器。因此一定时间只能执行一段代码。执行javaScript代码就以偶一个队列,任务按照它们添加到队列的顺序执行。第二哥参数告诉javaScript多长时间添加到队列。如果队列是空就立即执行,如果不是空,等待前面的代码执行完毕之后在执行。
2)location 对象
// location对象属性
location.hash //("#contents")url的hash,如果url中不包含,则返回空字符串
location.host // ("www.baidu.com:80") 服务器名称和端口号(如有)
location.hashname // ("www.baidu.com") 服务器名称(没有端口号)
location.href(location.toString()) // ("https://www.beidu.com")当前加载页面完整的url。
location.pathname // ("/wiley/"") url中目录或文件名
location.port //("8080")url中的端口号,如没有,则返回空字符串
location.protocol //("http")查看页面使用协议。通常是"http"或者"https"
location.search //("?name=vivian")url查询字符串。这个符号以问号开头
使用上述方法修改url都会在浏览器中生成一条记录。
1.获取字符串参数
// 获取url头部参数
function getStringParameter(text = null) {
let urlText = "";
if (text) {
// 截取传入的字符串
urlText = text.includes("?") ? text.split("?")[1] : "";
} else {
// 获取当前页面url并判断是有带有参数
urlText = location.search.length ? location.search.substring(1) : "";
}
// 截取分割的url
let urlArr = urlText.length ? urlText.split("&") : [];
// 装载url数据的容器
let urlObj = {};
urlArr.forEach((item) => {
let itemsArr = item.split("=");
// 处理文字
let name = decodeURIComponent(itemsArr[0]);
let value = decodeURIComponent(itemsArr[1]);
if (name.length) {
urlObj[name] = value;
}
});
return urlObj;
};
2.位置操作
显示当前窗口加载的文档信息和一些导航功能。
window.location = "https://www.baidu.com/";
location.href = "https://www.baidu.com/";
location.reload(); //重新加载(可能从缓存加载)
location.reload(true); // 重新加载(从服务器加载)
3)navigator 对象
用来识别浏览器的标准。
navigator.appCodeName:返回浏览器的代码名。
navigator.appMinorVersion:返回浏览器的次级版本。
navigator.appName:返回浏览器的名称。
navigator.appVersion:返回浏览器的平台和版本信息。
navigator.browserLanguage:返回当前浏览器的语言。
navigator.cookieEnabled:返回指明浏览器中是否启用 cookie 的布尔值。
navigator.cpuClass:返回浏览器系统的 CPU 等级。
navigator.onLine:返回指明系统是否处于脱机模式的布尔值。
navigator.platform:返回运行浏览器的操作系统平台。
navigator.systemLanguage:返回 OS 使用的默认语言。
navigator.userAgent:返回由客户机发送服务器的 user-agent 头部的值。
navigator.userLanguage:返回 OS 的自然语言设置。
navigator.javaEnabled():规定浏览器是否启用 Java。
navigator.taintEnabled():规定浏览器是否启用数据污点 (data tainting)。
// 插件检查
function hasPlugin(name) {
const _name = name.toLowerCase();
console.log(navigator.plugins);
let leng = navigator.plugins.length;
// for(let i = 0; i < leng; i+1 ){
for(let i = 0; i < leng; i+1 ){
if(navigator.plugins[i].name.toLowerCase().includes(name)){
return true
}else {
return false
}
}
}
console.log(hasPlugin("Flash"));
4)screen 对象
screen对象用处不大,基本上只是表达客户端能力(而且很多方法被浏览器屏蔽)。
5)history 对象
该对象保存着用户的上网记录。
// 后退一页
history.go(-1);
history.back();
// 前进一页
history.go(1);
history.forward();
三.客户端检测
// 用户代理字符串检测(可以检测引擎、平台、操作系统、移动设备、游戏平台)
const client = function() {
//呈现引擎
let engine = {
ie: 0,
gecko: 0,
webkit: 0,
khtml: 0,
opera: 0,
// 完整版本号
ver: null
};
// 浏览器
let browser = {
// 主要浏览器
ie: 0,
firefox: 0,
safari: 0,
konq: 0,
opera: 0,
chrome: 0,
// 具体版本号
ver: null
};
// 平台/设备/操作系统
let system = {
win: false,
mac: false,
x11: false,
// 移动设备
iphone: false,
ipod: false,
ipad: false,
ios: false,
android: false,
nokiaN: false,
winMobile: false,
// 游戏系统
wii: false,
ps: false
};
// 检测呈现引擎和浏览器
let ua = navigator.userAgent;
if (window.opera) {
engine.ver = browser.ver = window.opera.version();
engine.opera = browser.opera = parseFloat(engine.ver);
} else if (/AppleWebKit\/(\S+)/.test(ua)) {
// \S 匹配一个非空白字符
engine.ver = RegExp["$1"];
engine.webkit = parseFloat(engine.ver);
// 确定是chrome还是safari
if (/Chrome\/(\S+)/.test(ua)) {
browser.ver = RegExp["$1"];
browser.chrome = parseFloat(browser.ver);
} else if (/Version\/(\S+)/.test(ua)) {
browser.ver = RegExp["$1"];
browser.safari = parseFloat(browser.ver);
} else {
//近似的确定版本号
let safariVersion = 1;
if (engine.webkit < 100) {
let safariVersion = 1;
} else if (engine.webkit < 312) {
let safariVersion = 1.2;
} else if (engine.webkit < 412) {
let safariVersion = 1.3;
} else {
let safariVersion = 2;
}
browser.safari = browser.ver = safariVersion;
}
} else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)) {
engine.ver = browser.ver = RegExp["$1"];
engine.khtml = browser.konq = parseFloat(engine.ver);
} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) {
engine.ver = RegExp["$1"];
engine.gecko = parseFloat(engine.ver);
// 确定是不是firefox
if (/Firefox\/(\S+)/.test(ua)) {
browser.ver = RegExp["$1"];
browser.firefox = parseFloat(browser.ver);
}
} else if (/MSIE ([^;]+)/.test(ua)) {
engine.ver = browser.ver = RegExp["$1"];
engine.ie = browser.ie = parseFloat(engine.ver);
}
// 检测浏览器
browser.ie = engine.ie;
browser.opera = engine.opera;
// 检测平台
let p = navigator.platform;
system.win = p.includes("Win");
system.mac = p.includes("Mac");
system.x11 = (p === "x11") || (p.includes("Linux"));
// 检测window操作系统
if (system.win) {
if (/Win(?:dows)?([^do]{2})\s?(\d+\.\d+)?/.test(ua)) {
if (RegExp["$1"] === "NT") {
switch(RegExp["$2"]) {
case "5.0":
break;
case "5.1":
system.win = "XP";
break;
case "6.0":
system.win = "Vista";
break;
case "6.1":
system.win = "7";
break;
default:
system.win = "NT";
break;
}
} else if (RegExp["$1"] === "9x") {
system.win = "ME";
} else {
system.win = RegExp["$1"];
}
}
}
// 移动设备
system.iphone = ua.includes("iPhone");
system.ipod = ua.includes("iPod");
system.ipad = ua.includes("ipad");
system.nokiaN = ua.includes("NokiaN");
// windows mobile
if (system.win === "CE") {
system.winMobile = system.win;
} else if (system.win === "Ph") {
if (/Windows Phone OS (\d+.\d+)/.test(ua)) {
system.win = "Phone";
system.winMobile = parseFloat(RegExp["$1"]);
}
}
// 检测ios版本
if (system.mac && ua.includes("Mobile")) {
if (/CPU (?:iphone)?OS (\d+_\d+)/.test(ua)) {
system.ios = parseFloat(RegExp.$1.replace("_", "."));
} else {
system.ios = 2; //不能正确检测出来,只能猜测
}
}
// 检测android
if (/Android (\d+\.\d+)/.test(ua)) {
system.android = parseFloat(RegExp.$1);
}
// 游戏系统
system.wii = ua.includes("Wii");
system.ps = /playstation/i.test(ua);
// 返回检测对象
return {
engine: engine,
browser: browser,
system: system
}
}();
四.DOM
早起的IE浏览器的DOM是基于COM实现的,和同时期的浏览器有很大的差异
节点层次
- 节点关系
每个节点都会有childNodes属性,其中保存着一个NodeList对象(类数组对象)。由此我们可以将NodeList转化为数组对象。但是早期的IE浏览器是基于COM来实现DOM的,所有我们有如下方法:
function converToArray(nodes) {
var array = null;
try{
// 针对非早期IE浏览器
array = Array.prototype.slice.call(oBody.childNodes, 0);
}catch (e){
// 早期IE检查
array = new Array();
for(var i = 0; i < nodes.length; i += 1){
array.push(nodes[i]);
}
}
return array;
};
另外我们可以使用hasChildNodes() ,来查看元素下有子节点(有文字也算文字节点):
console.log(html.hasChildNodes());
- 操作节点
// 向目标元素末尾插入一个子节点
someNode.appendChild(nodes);
// 在指定位置插入节点
someNode.insertBefore(nodes, someNode.childNodes[ number ]);
// 替换某个节点
someNode.replaceChild(nodes, someNode.lastChild); // 替换最后一个
// 移除某个节点
someNode.removeChild(nodes);
// 找到所有form元素
document.forms
// 找到所有img
dcument.images
// 获取元素上的属性
var oText = someNode.getAttribute('data-ti');
// 设置节点属性(IE8以前不支持设置style和class,不生效)
someNode.setAttribute('data-titit',"title");
// 删除节点属性(IE6不支持)
someNode.removeAttribute("data-ti");
// 用attrValue遍历属性
function outputAttributes(el) {
let pairs = [];
let [attrName, attrValue] = Array(2).fill(null);
let arr = el.attributes;
for (let item of arr) {
attrName = item.nodeName;
attrValue = item.nodeValue;
pairs.push(`${attrName}="${attrValue}"`)
}
return pairs.join(" ");
}
// 创建元素节点
docment.createElement("div");
// 创建文本节点
docment.createTextNode("text");
四.DOM扩展
1)选择符API
// 参数为css选择符,返回与该模式匹配的第一个元素,没有找到返回null
querySelector()
// 返回一个NodeList实例,如果没有找到匹配元素返回NodeLise是空
querySelectorAll()
2)元素遍历
// IE9以上
// 返回子元素的个数
1.childElementCount
// 指向第一个子元素
2.firstElementChild
// 指向最后一个子元素
3.lastElementChild
// 指向前一个同辈元素
4.previousElementSibling
// 指向后一个同辈元素
5.nextElementSibling
3)html5
// 返回指定所有元素的NodeList
docment.getElementsByClassName();
// 获取焦点
el.focus();
// 获取焦点的元素,文档加载完成时,是body元素的引用,文档加载期间的值为null
docment.activeElement;
// 确定文档是否获取了焦点
docment.hasFocus();
1.HTMLDocument的变化
document.onreadystatechange = function () {
switch (document.readyState) {
case "uninitialized":
// XML 对象被产生,但没有任何文件被加载,即处于“还未开始载入”。
break;
case "loading":
// 表示文档还在加载中,即处于“正在加载”状态。
break;
case "interactive":
// 文档已经结束了“正在加载”状态,DOM元素可以被访问。
// 但是像图像,样式表和框架等资源依然还在加载。
break;
case "complete":
// 页面所有内容都已被完全加载.
break;
default:
}
}
2.插入标记
innerHTML
该属性返回与调用元素的所有子节点对应的HTML标记。(返回元素的所有子集)
outerHTML 该属性返回调用它的元素及所有子节点对应的HTML标记。(返回包含自身的所有元素) insertAdjacentHTML()
// 作为前一个同辈元素被插入
el.insertAdjacentHTML('beforebegin', '<p>hello world</p>');
// 作为第一个子元素被插入
el.insertAdjacentHTML('afterbegin', '<p>hello world</p>');
// 作为最后一个子元素被插入
el.insertAdjacentHTML('beforeend', '<p>hello world</p>');
// 作为后一个同辈元素被插入
el.insertAdjacentHTML('afterend', '<p>hello world</p>');
3.scrollIntoView()
该方法是在HTML元素上调用。通过滚动浏览器或者某个元素,调用的元素出现在视口中。
4)专有扩展
contains()
判断某个节点是否是另一个节点的后代
body.contains(div); // 如果childEl是parentEl的后代,则返回true
五.DOM2和DOM3
1)操作样式表
- insertRule() 向现有的样式表添加新的css。
var sheet = document.styleSheets[0];
sheet.insertRule("body {background: #fff}",0);
- addRule()
// 仅对IE有效
var sheet = document.styleSheets[0];
sheet.addRule("body"," {background: #fff}",0);
- deleteRule(number); 从样式表中删除css,接受的删除是要删除样式的位置。
sheet.deleteRule(0);
- removeRule(number);
// 仅对IE有效
sheet.removeRule(0);
2)元素大小
1.偏移量
// 获取元素在垂直方向向上占用的空间大小,单位: px。
el.offsetHeight;
// 获取元素在水平向上占用的大小,单位:px。
el.offsetWidht;
// 获取元素的左外边框至包含元素的左内边框之间的像素距离。
el.offsetLeft;
// 获取元素的上外边框至包含元素的上内边框之间的像素距离。
el.offsetTop;
// 获取某个元素在页面上的左偏移量(跨浏览器兼容)
function getElementLeft(el){
let actualLeft = el.offsetLeft;
let current = el.offsetParent;
while(current !== null){
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
}
// 获取某个元素在页面上的上偏移量(跨浏览器兼容)
function getElementTop(el){
let actualTop = el.offsetTop;
let current = el.offsetParent;
while(current !== null){
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
2.滚动区大小
// 获取视口高度
var docHeight = Math.max(document.documentElement.scrollHeight,document.documentElement.clientHeight);
// 获取视口宽度
var docWidth = Math.max(document.documentElement.scrollWidth,document.documentElement.clientWidth);
在混乱模式的IE浏览器下(IE6,IE7)需要用document.body来代替document.documentElement。
var docHeight = Math.max(document.body.scrollHeight,document.body.clientHeight);
var docHeight = Math.max(document.body.scrollWidth,document.body.clientWidth);
3.确定元素大小
// 方法返回是一个具有四个属性 left、top、right、bottom 的 DOMRect 对象。
Element.getBoundingClientRect()
这个方法返回的其实是6个值left、top、right、bottom、x、y。但是在IE的浏览器下x、y是没有的。详情可以参照MDN的文档:developer.mozilla.org/zh-CN/docs/…
4.遍历DOM
这个功能用的实在是很少没有相关资料。我在晚上找到一篇文章可以参考:www.cnblogs.com/venoral/p/5…
1.NodeIterator
// 创建一个NodeIterator对象
var iterator = document.createNodeIterator(Element,NodeFilter.SHOW_ALL,null,false)
2.Treewalker
// 创建一个Treewalker对象
var walker = document.createTreeWalker(Element,NodeFilter.SHOW_ALL,null,false);