回顾:Object为对象类,所有的对象数据类型都是它的实例
同理,Function为函数类,所有的函数数据类型都是它的实例
同时,Function的原型上有call,apply,bind...方法
Object是Function的实例,Function.prototype.__proto__是Object的实例
Function.prototype.__proto__指向Object.prototype
function Fn(){
this.x = 100;
}
console.dir(Fn)
函数具有一些私有属性,如:
length:形参的个数;
name:函数名;
prototype:类的原型,在原型上定义的方法,都是当前Fn类实例的公有方法;
__proto__:把函数当做一个普通对象,指向Function的原型.
Fn.__proto__.__proto__也指向Object
所有的函数都是函数类的一个实例,也作为一个实例的存在,实例就是对象数据类型的
一个函数存在了多面性:
1.本身是一个普通函数,执行时形成私有作用域(闭包),形参赋值、预解释、代码执行、栈内存释放;
2.本身是一个类,有自己的实例,也有prototype属性是自己的原型,它的实例都可以指向原型;
3.本身是一个普通对象,作为对象可以有自己的私有属性,也可以通过__proto__找到Function.prototype(函数的三种角色)
但是三者之间没有必然关系
function Fn(){
var num = 500;
this.x = 100;
} //作为函数
Fn.prototype.getX = function(){
console.log(this.x);
}
Fn.aaa = 1000; //作为对象
var f = new Fn; //作为类
console.log(f.num);
console.log(f.aaa);
var res = Fn();
console.log(res);
console.log(Fn.aaa);
Function.prototype是函数数据类型的值,但是相关操作和之前的对象一样
叫做Empty/anonymous(匿名)
console.log(Function.prototype);
//function(){}
Function类的原型为一个空函数
语法:call([thisObj[,arg1[, arg2[,[,.argN]]]]])
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
说明:call方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
举例模仿call原理:
var arr = [12,13,14,15];
Array.prototype.slice = function(context){
//1.将函数中的this变为context
//2.this方法执行
this();
}
//arr.slice arr实例通过原型链查找机制找到位于Array.prototype上的slice方法
//arr.slice(); 让找到的slice方法执行,在执行的过程中对arr进行了截取操作
call()改变this关键字
Function.prototype.call = function(){}
var obj = {name:"Amy"};
function fn(){
console.log(this);
}
fn();
fn.call(obj);
call:首先让原型上的call方法执行,在执行call方法时让fn的this变为第一个参数obj,然后再把fn执行
function f1(){
console.log(1);
}
function f2(){
console.log(2);
}
f1.call(f2); //1
f1.call.call(f2); //call方法的this为f1.call
f1.call.call.call.call(f2);
f1通过原型链找到的是Function.prototype的call方法,然后再让call方法找到Function.prototype 因为本身的值为一个函数,所以同样可以找到Function原型,在第二次找到call的时候让方法执行,方法中的this为f1.call,首先让这个方法中的this变为f2,再让f2执行
f1.call只是起到了查找的作用
apply和call的方法作用是一模一样的,都是用来改变方法的this关键字,并且把方法执行
;
在严格模式和非严格模式下下apply和call调用null,undefined的情况也是一样的
var obj = {name:"Amy"}
function fn(num1,num2){
console.log(num1+num2);
console.log(this);
}
fn.call(obj,100,200);
fn.call(100,200); //NaN,undefined
fn.apply(obj,[100,200]);
call是一个个传递值的;apply是传递两个值,将函数参数统一放到数组中进行操作,也相当于一个个赋值
function fn(){
console.log(this);
}
//非严格模式下
fn.call(); //this->window
fn.call(null); //this->window
fn.call(undefined); //this->window
"use strict";
//严格模式
fn.call(); //this->undefined
fn.call(null); //this->null
fn.call(undefined); //this->undefined
严格模式下自执行函数的this永远是undefined
严格模式下函数执行的"."前没有对象this是undefined(非严格模式下是window)
非严格模式下没有写执行主体的情况下this都为window
非严格模式下函数执行前的"."的元素,如果没有则为window.
这个方法在IE6/7/8不兼容;和call、apply相似,都是改变this的
var obj = {name:"Amy"};
function fn(){
console.log(num1+num2,this);
}
fn.bind(obj,1,2);
bind只是改变函数中的this,并且给fn传递了参数,但是此时并没有执行fn,所以不输出任何内容
var temp = fn.bind(obj,1,2);
temp();
执行bind会有一个返回值
bind有预处理思想,事先把this改变为想要的结果,并把需要的值准备好,如果需要再直接执行即可
回顾this的四种情况:
1)自执行函数的this是window
2)给元素的某一行为绑定方法,this为当前元素
3)方法执行前的"."元素
4)构造函数中this就是当前实例
但是当前四种情况遇到call和apply的时候统统让路
求数组最大值和最小值的几种方法:
现将数组排序,然后取第一个值和最后一个值
一个括号中出现多项表达式,中间以逗号隔开,但是只能获取到最后一项,此为括号表达式
括号表达式:(x1,x2,x3...)括号表达式中出现多项内容时,并以逗号隔开时,只能获取或执行最后一项
function f1(){
console.log(this);
}
function f2(){
console.log(this);
}
var obj = {name:"Amy",fn:f2}
;(f1,f2)(); //window
;(f1,obj.f2)(); //window
;(obj.f2)(); //obj
;(f2)(); //window
假设数组第一个值为最大值,然后和后面的值逐个比较,遇到比假设值大的则替换,直到结束
var min = arr[0];
var max = arr[0];
for(var i=0;i<arr.length;i++){
var cur = arr[i];
cur>max ? max = cur : null;
cur<min ? min = cur : null;
}
假设法类似自定义属性,这都是编程中的常用思想
var arr = [12,15,46,53,32,16,33];
var a = Math.max.apply(null,arr);
两种方法的比较:
var arr = [];
for(var i=0;i<arguments.length;i++){
arr[arr.length] = arguments[i];
}
//模拟slice方法复制数组
Array.prototype.mySlice = function(){
var arr = [];
for(var i=0;i<this.length;i++){
arr[arr.length] = this[i];
}
return arr;
};
由此可以得出将slice方法中的this替换为arguments即可,得出
Array.prototype.slice.call(arguments);
类数组使用数组的方法,需要转化
function avg(){
Array.prototype.sort.call(arguments,function(a,b){return a-b;});
[].pop.call(arguments);
[].shift.call(arguments);
return (eval([].join.call(arguments,"+"))/arguments.length).toFixed(2);
};
var res = avg(1,4,2,6,3,6,8,7,4,9);
console.log(res);
Number.toFixed(2) 保留小数两位
try{
console.log(num)
}catch(e){
console.log(e.message) //收集当前代码报错原因
}
console.log("num")
用try-catch捕获异常信息不影响后面的代码执行 如果try部分代码报错,会默认执行catch部分的代码
try{
...
}catch(){
...
}finally{
//无论try代码是否报错,这部分代码一定执行
}
需求:既要捕获异常信息,又要让下面的代码不执行
那么手动抛出一条异常信息,以终止代码执行即可
try{
console.log(num);
}catch(e){
throw new Error("当前网繁忙,请稍后再试")
//new ReferenceError 引用错误
//new TypeError 类型错误
//new RangeError 范围错误
}
兼容方法,将类数组转化为数组
var arr = [];
try{
arr = Array.prototype.slice.call(likeArray);
}catch(e){
for(var i=0;i<likeArray;i++){
arr[arr.length] = likeArray[i];
}
}
把一个方法A当做参数值,传递给另外一个函数B,在B执行的过程中随时根据需求让A执行
function fn(callback){
callback();
}
fn(function(){});
var arr = [1,9,2,8,3,8,4,7,5,6];
arr.sort(function(a,b){
//每次执行匿名函数时,a为当前项,b为当前项的后一项
//console.log(a,b);
return a-b;
return b-a;
//return 的目的是返回一个大于0或小于0的数,大于0时a,b交换位置;小于0时a,b不变
});
var arr = [
{name:"Amy",age:12},
{name:"Bob",age:10},
{name:"Cindy",age:14},
{name:"Darin",age:11}
];
arr.sort(function(a,b){
//按照年龄排序
return parseFloat(a.age) - parseFloat(b.age);
//按照字母排序
return a.name.localeCompare(b.name);
});
console.log(arr);
stringObject.localeCompare(target);
用本地特定的顺序来比较两个字符串:
如果 stringObject 小于 target,则localeCompare()返回小于0的数。
如果 stringObject 大于 target,则该方法返回大于0的数。
如果两个字符串相等,或根据本地排序规则没有区别,该方法返回 0。
返回值:
0 : 字符串匹配100%
1 : 不匹配,参数值来自于语言环境的排序顺序字符串对象的值之前
-1 : 不匹配,参数值来自于语言环境的排序顺序字符串对象的值之后
JSON格式:属性名被双引号包起来,且必须被双引号包起来才行
//JSON格式对象
var jsonObj = {"name":"Amy","age":12};
window对象中提供了JSON属性,并有两个方法:
JSON.parse(); 把 JSON格式的字符串 转化为 JSON格式的对象
JSON.stringify(); 把 JSON格式的对象 转化为 JSON格式的字符
在IE6、7中window下没有JSON对象,也就没有了parse和stringify方法
把JSON格式的字符串转换为JSON格式对象 的兼容方法:
var str = '{"name":"Amy","age":12}';
eval("("+ str +")") //务必手动添加小括号
警告:关于JSON和eval需要注意的是:在代码中使用eval是很危险的,特别是用它执行第三方的JSON数据(其中可能包含恶意代码)时,尽可能使用JSON.parse()方法解析字符串本身。该方法可以捕捉JSON中的语法错误,并允许你传入一个函数,用来过滤或转换解析结果。如果此方法以备Firfox 3.5 、IE8 及 Safari 4 原生支持。大多数javascript类库包含的JSON解析代码会直接调用原生版本,如果没有原生支持的话,会调用一个略微不那么强大的非原生版本来处理。
回流(reflow):当页面中的html结构发生改变(增加、删除元素,位置移动)的时候,浏览器都需要重新进行计算DOM结构,重新对页面渲染
重绘(repaint):某一个元素部分样式改变了,浏览器只重新渲染这个元素
动态创建内容并添加到页面中去的方法:
方法1:动态创建元素节点并追加到页面的方式实现数据绑定
优势:把创建的内容追加到页面中,对原有内容没有影响
弊端:每创建一个元素添加到页面中的时候,就引发一次回流,影响性能
方法2:字符串拼接
优势:事先把内容拼接好,最后统一添加到页面中,只引发一次回流
弊端:将新拼接的字符串添加到原有
结构中,原有
绑定事件都取消了
字符串拼接 是工作中最常用的绑定数据方式
模板引擎数据绑定:jade,kTemplate,angularJS,backboneJS...原理都是字符串拼接
var frag = document.createDocumentFragment();
创建文档碎片相当于临时创建一个容器的对象
createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法
文档碎片的作用:每次JavaScript对DOM的操作都会改变页面的变现,并重新刷新整个页面,从而消耗了大量的时间。为解决这个问题,可以创建一个文档碎片,把所有的新节点附加其上,然后把文档碎片的内容一次性添加到document中。
使用完毕后
frag = null;
释放占用内存,使页面更干净
经IE和FireFox下测试,在append1000个元素时,效率能提高10%-30%,FireFox下提升较为明显。
不要小瞧这10%-30%,效率的提高是着眼于多个细节的,这将是一个质的飞跃,您也将步入骨灰级玩家的行列
页面中的标签和JS中获取到的元素对象或者元素集合是绑定在一起的;页面中的html结构改变了,JS中不需要重新获取,集合里面的内容也会自动改变
<ul class="list">
<li>98</li>
<li>99</li>
<li>96</li>
<li>97</li>
</ul>
var frag = document.createDocumentFragment();
for(var i=0;i<arr.length;i++){
frag.appendChild(arr[i]);
}
oUl.appendChild(frag);
frag = null;
//现在应该是8条数据才对,但是只有4条
获取table表格的各个元素方法
tableObject.rows 集合返回表格中所有行的一个数组
tableObject.cells 集合返回表格中所有单元格的一个数组
tableObject.tHead 集合返回表格中表头元素
tableObject.tBodies 集合返回表格中tbody元素集合
<table class="tab">
<thead>
<tr>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
</tr>
</tbody>
</table>
var tab = document.getElementById("tab");
var oHead = tab.tHead; //获取thead元素
var oThs = oHead.rows[0].cells; //获取第一行多个th元素
var oBody = tab.tBodies[0]; //获取tbody元素
var oTrs = oBody.rows; //获取多个tr元素
document.getElementsByTagName("div")
document.getElementsByClassName("div")
获取的是HTMLCollection元素集合类的实例
document.getElementsByName("list")
document.querySelectorAll("list")
获取的是NodeList节点集合类的实例
document.getElementById("lsit");
获取的是HTMLDivElement元素
var oList = document.getElementsByTagName("div");
var arr = Array.prototype.slice.call(oList);
在IE8及以下不支持借用数组slice将元素集合
或节点集合
的类数组转化为数组的方法
但是arguments借用数组的方法不存在兼容问题