。仔细复习以下内容
首先:指定argument为this的(arguments[0]和obj.func是一样的道理,所以this是arguments);然后:this.foo的问题,因为直接调用main,从而this是window;isInt/isFloat等方法
。if,for,while都会变量提升
console.log("a",a);//undefined
console.log("a in window","a" in window);//true
if('a' in window){
var a=100;
}
while也有变量声明提升(但是函数不会提升哦):
console.log("a",a);//undefined
console.log("a in window","a" in window);//true
var i=1;
//while也会变量声明提升,但是while里面函数不会
while('a' in window){
var a=10;
i++;
console.log('i',i);
if(i==10)break;
}
for和if一样会变量声明提升:
console.log("a",a);//报错
console.log("a in window","a" in window);//false
while('a' in window){
console.log('while');
}
。快速排序方法(时间复杂度为nlongn)
var quickSort = function(arr) {
//单个元素不需要排序
if (arr.length <= 1) {
return arr;
}
var pivotIndex = Math.floor(arr.length / 2);
//获取转轴元素
var pivot = arr.splice(pivotIndex, 1)[0];
//splice方法得到删除的那个元素是多少
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++){
//小于的放在left中
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
//否则放在right数组中
right.push(arr[i]);
}
}
//左边调用quickSort方法,同时和轴点元素和右边调用quickSort方法的数组结合就行
return quickSort(left).concat([pivot], quickSort(right));
};
console.log('排序后结果',quickSort([3,4,2,1,0]));
。字符串运算的问题(必须转化为十进制的,不管是加号还是减号的问题;而且对于字符串来说转化为数字默认是10进制的)
'020'+010//010是8进制的,所以换算为十进制的8,因此连接起来就是"0208"
'020'-010//减法只要有一个非数字就转化为数字,而020会转化为10进制,所以最后就是20-8=12
注意:字符串形式数字,如果要转化为数字,那么我们直接去掉前面的0,当作十进制就可以了。
。函数参数传入的bb相当于重新定义了bb,然后给bb赋值为2,所以打印2,1
var bb = 1;
function aa(bb) {//外面的参数和形参同名的情况下,这时候就相当于var bb=1,bb=2
bb = 2;
alert(bb);//所以这里打印局部变量2
};
aa(bb);
alert(bb);//因为里面是局部变量,所以这里是1,也就是局部变量不会对外面有影响
。题目2:函数里面对bb的修改不会对全局变量产生影响(前提要有var bb重新定义的同名变量才行,如果没有bb那么就是下面这种题目3)
var bb = 1;
function aa(zz) {//这里的形参和外部的全局变量不同,所以相当于定义了var zz=1
bb=4;
var bb=3;
//只要有var在函数里面,就不会修改外面的全局变量的值的
//这里相当于var bb=3,bb=5所以里面的局部变量的bb成了5
//因为里面有var所以就有了变量类型声明提升的,所以一开始就在函数的最上面有了var bb=undefined了
bb=5;
console.log(bb);
};
aa(bb);
console.log(bb);
//打印5,1
这里的结果是5,1其测试的知识点还是我们熟悉的,也就是变量类型声明提升的概念!和下面的函数输出是同样的含义
var bb = 1;
function aa(zz) {
//这里的形参和外部的全局变量不同,所以相当于定义了var zz=1
bb=4;
bb=5;
var bb=3;
console.log(bb);
};
aa(bb);
console.log(bb);
//打印3,1
。题目3:如果函数里面没有var定义同名变量,那么就是会被修改。
var bb = 1;
function aa(zz) {
bb=4;
bb=5;
console.log(bb);
};
aa(bb);
console.log(bb);
//打印5,5
。题目4:闭包会被重置
function Foo() {
var i = 0;
return function() {
console.log(i++);
}
}
var f1 = Foo(),
f2 = Foo();
f1();
f1();
f2();
//打印0,1,0
。问题5:setTimeout部分的输出?其会等待所有的js代码运行过后才会执行我们的settimeout中的代码
for(var i=1;i<=3;i++){
setTimeout(function(){
console.log(i);
},0);
};
//原因:Javascript事件处理器在线程空闲之前不会运行。追问,如何让上述代码输出1 2 3?
for(var i=1;i<=3;i++){
setTimeout((function(a){
//改成立即执行函数
console.log(a);
})(i),0);
};
。如何获取一个字符串出现的次数:exec还是match?如果使用exec那么每次返回的是一个数组,其第一个元素是匹配的结果,这个数组有一个index表示匹配的下标,这时候的正则对象有一个lastIndex表示当前已经查询到的下标。
var str="my name is qinliang, he is fkl";
//不断调用exec方法是获取到所有信息的
var reg=/is/g,k=0;
//这里k必须赋值,否则他开始就是undefined,后面的自增运算就是会错的!
while((a=reg.exec(str))!=null)
{
console.log(a[0]+"这次的下标"+a.index+"下一个下标:"+reg.lastIndex);
//这里是RegExp的lastIndex!
k++;
}
console.log(k);
var str="my name is qinliang, he is fkl";
var times=str.match(/is/g).length;
//用match获取匹配的次数
console.log(times);
。如何让一个方法返回多个值?
解答:通过返回数组或者返回对象的形式
如何创建一个数组迭代器?
var arr=['a','b','c','d','e','f','g'];
//创建Array迭代器,也就是每次从数组中迭代出来一个元素
function arrayIterator(arr)
{
var x=0;
return function(){
return arr[x++];
}
}
var current,iterator=arrayIterator(arr);
while(current=iterator())
{
//迭代器
console.log(current);
}
如何防止自己的页面被iframe?或者self===top也可以的!
if(top.location != self.location){
top.location = self.location;//防止页面被框架包含
}
如何获取自己的年龄?
function getAge(dateString) {
var today = new Date();
var birthDate = new Date(dateString);
//通过new Date来通过日期字符串创建一个Date对象!
var age = today.getFullYear() - birthDate.getFullYear();
var m = today.getMonth() - birthDate.getMonth();
//如果月数小于0,那么年龄相减,如果月数等于0那么看天数!
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
}
console.log(getAge("1990,9,28"));//6
。javascript字符和ASCII之间的转化?(charCodeAt和fromCharCode就可以了)
//javascript字符串和ASCII之间转化
var str="qinliang";
console.log(str.charCodeAt(0));
console.log(String.fromCharCode(str.charCodeAt(0)));
。怎么判断浮点数相等?
console.log(0.1+0.2 == 0.3);//false
console.log(Math.abs(0.1+0.2 - 0.3) < 0.000001);//true
replace方法是如何被用于正则表达式的?
。如何解决js对象克隆的方法:
function copy(o){
var copy = Object.create( Object.getPrototypeOf(o) );
//getOwnPropertyNames如果用于实例那么获取所有的实例属性,如果用于prototype那么就是获取所有的原型属性!和Object.keys一样!
var propNames = Object.getOwnPropertyNames(o);
//迭代属性和方法
propNames.forEach(function(name){
//可以获取给定属性的描述符,返回值是一个对象,如果是访问器属性(对象)那么有configurable,enumerable,get,set,如果是数据属性那么有
//configurable,enumberable,writable,value属性!
var desc = Object.getOwnPropertyDescriptor(o, name);
//把这些方法放在copy上面!
Object.defineProperty(copy, name, desc);
});
return copy;
}
var o1 = {a:1, b:2,c:{name:"xxx",sex:"female"}};
var o2 = copy(o1); // o2 looks like o1 now
alert(o2.c.sex);
可以实现数组和对象的克隆,或者采用如下的方式
//js对象克隆
function clone(obj){
if(obj===null||typeof obj!='object')
return obj;
if(obj instanceof Date){
var date=new Date();
date.setTime(date.getTime());//new Date().getTime也可以是Date.now或者+new Date()
return date;
}
if(obj instanceof Object){
//深度克隆才行
var copy=new Object();
for(var name in obj){
if(typeof obj[name]==='object'){
//如果是对象,那么我们还是要在obj上面保存,如obj={name:"qinlaing",school:{name:"DLUT",location:"DL"}}
copy[name]=arguments.callee(obj[name]);
}else{
copy[name]=obj[name];
}
}
return copy;
}
if(obj instanceof Array){
var copy=[];
//如["name","sex","female",{school:"DLUT"}]
for(var i=0;i
。增强版提取URL中参数(第一个是非&和非=号,第二个是非&号,而且要是捕获组)
function getQueryString() {
var result = {}, queryString = location.search.substring(1),
re = /([^&=]+)=([^&]*)/g, m;
while (m = re.exec(queryString)) {
result[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
}
return result;
}
。检查一个数是否为浮点数(如果小数点后都是0,那么两个函数都会被当做整数处理的,而不是浮点数)
//注意:这两个函数如果小数点后都是000,那么会被当做整数处理的,而不是浮点数
function isInt(n) {
return typeof n === 'number' && parseFloat(n) == parseInt(n, 10) && !isNaN(n);
}
function isFloat (n) {
return n===+n && n!==(n|0);
}
console.log('int=',isInt(12.000));//判断是真的
console.log('isFloat=',isFloat(23.01));//判断是真的
。javascript添加style节点(通过styleSheet.cssText,style.appendChild(document.createTextNode))
var css = 'h1 { background: red; }',
head = document.getElementsByTagName('head')[0],
style = document.createElement('style');
style.type = 'text/css';
if(style.styleSheet){
style.styleSheet.cssText = css;
}else{
style.appendChild(document.createTextNode(css));
}
head.appendChild(style);
。取得两个数组的交集
function unique(arr){
//数组去重[1,2,3,2,2],result[1]=1,result[3]=3,result[2]=2,result[100]=100;
var result=[],retV=[];
for(var j=0;j
。统计字符串出现的次数
var temp = "This is a string.";
var count = temp.match(/is/g).length;
。跨平台的事件
function addEventHandler(oTarget, sEventType, fnHandler){
if(oTarget.addEventListener){
oTarget.addEventListener(sEventType,fnHandler,false);
} else if(oTarget.attachEvent){
oTarget.attachEvent("on"+sEventType,fnHandler);
} else{
oTarget["on"+sEventType]=fnHandler;
}
}
。javascript实现endWith
String.prototype.endWith=function(suffix){
return this.substring(this.length-suffix.length).toLowerCase()===suffix.toLowerCase();//记得toLowerCase才行
}
也可以调用数组的splice方法
String.prototype.endWith=function(suffix){
var len=this.length;//ql.txt
var suffixLen=suffix.length;
var result=this.split('');
result.splice(0,len-suffixLen);
if(result.join('').toLowerCase()===suffix.toLowerCase()){
return true;
}else{
return false;
}
}
正则表达式也是可以的:见自己的博客
String.prototype.endWith=function(str){
return new RegExp(str+"$",'i').test(this);
//这里不能用exec和match方法,因为他们在非全局的时候返回的是一样的
//这里如果要用test方法必须使用RegExp这个构造函数,因为只有这个构造函数能够接受字符串,而像正则表达式字符串其不能使用字符串拼接,否则拼接后成了字符串而不是正则表达式对象了
}
console.log(new String('input.txt').endWith('txt'));
。通过原型继承来创建一个对象
function inherit(p){
if(!p){
throw TypeError("p is not an object or null");
}
if(Object.create){
return Object.create(p);
}
var t=typeof p;
if(t !== "object" && t !== "function"){
throw TypeError("p is not an object or null");
}
function f(){};
f.prototype=p;
return new f();
}
。解析URL参数的函数
function GetRequest() {
var url = location.search; //获取url中"?"符后的字串
var theRequest = new Object();
if (url.indexOf("?") != -1) {
var str = url.substr(1);
strs = str.split("&");
for(var i = 0; i < strs.length; i ++) {
theRequest[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]);
}
}
return theRequest;
}
。冒泡排序的方式
for (var i = 0; i < a.length - 1; i++) {
//比较的次数是length-1
for (var j = 0; j < a.length - 1 - i; j++) {
//内层循环是length-1-i次,这是和外面的循环唯一相关的部分
if (a[j] > a[j + 1]) {
//如果前面的元素比较大,那么前后进行交换,这一直是在内部完成的!
var tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
}
}
冒泡排序算法的运作如下:(从后往前)
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数Cmin=n-1和记录移动次数Mmin=0均达到最小值: , 。
所以,冒泡排序最好的时间复杂度为O(n)。
若初始文件是反序的,需要进行n-1 趟排序。每趟排序要进行n-i次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:
Cmax=(n-1+1)(n-1)/2
Mmax=3n(n-1)/2
冒泡排序的最坏时间复杂度为O(n2)。
综上,因此冒泡排序总的平均时间复杂度为O(n2)。
。选择排序(最后两句代码的顺序不能调换)
function sorrt(ary) {
length = ary.length;
//获取数组长度并且进行遍历
for (var i = 0; i < length; i++) {
_min = ary[i];
//选择排序用来保存值
k = i
//选择排序用来保存下标
for (var j = i + 1; j < length; j++) {
//从外层循环i+1开始循环
if (_min > ary[j]) {
_min = ary[j]
//重新定义外层的值,这是较小的值
k = j
//重新定义外层的下标,该下标是较小的值的下标
}
}
//获取到了一次外部循环的最小的下标和最小的元素值!
//于是就把外层循环的i和内层循环获取到的最小下标和值换位置就可以了!
ary[k] = ary[i]
//把下标k的元素设置为我们现在的外层循环的元素
ary[i] = _min
//把外层的最小值,即下标i的元素设置为我们获取到的最小值,即_min!
}
return ary;
}
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。选择排序的交换操作介于0和(n − 1)次之间。选择排序的比较操作为n(n − 1) / 2次之间。选择排序的赋值操作介于0和3(n − 1)次之间。比较次数O(n^2),比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+...+1=n*(n-1)/2。 交换次数O(n),最好情况是,已经有序,交换0次;最坏情况是,逆序,交换n-1次。 交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。
。二分查找排序(必须是排序好了的数组才能排序,只要设置两个参数high和low两个指标就可以了,同时要注意h=lenth-1)
function binarySearch(data, dest){
var h = data.length - 1,
l = 0;
//循环到length-1,如果小于length-1就直接继续遍历
while(l <= h){
//获取中间的位置
var m = Math.floor((h + l) / 2);
if(data[m] == dest){
return m;
}
//如果比中间大那么就从中间往上一个查询,否则往下
if(dest > data[m]){
l = m + 1;
}else{
h = m - 1;
}
}
return false;
}
二分查找的基本思想是将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果xa[n/2],则只要在数组a的右半部搜索x.时间复杂度无非就是while循环的次数!
总共有n个元素,渐渐跟下去就是n,n/2,n/4,....n/2^k(接下来操作元素的剩余个数),其中k就是循环的次数由于你n/2^k取整后>=1
即令n/2^k=1可得k=log2n,(是以2为底,n的对数)所以时间复杂度可以表示O()=O(logn)
。如何获取浏览器的宽度和高度
function getViewPort()
{
if(document.compatMode=='BackCompat')//兼容模式
{
return
{
width:document.body.clientWidth,
height:document.body.clientHeight
}
}else//标准模式
{
return{
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight
}
}
}
。对于含有滚动条的元素来说是scrollWidth/scrollHeight,对于不含有滚动条的元素来说scrollHeight/clientHeight之间没有明确的关系,所以要做兼容,取两者之大
function getDocParameter()
{
if(document.compatMode=='BackCompat')//怪异模式
{
return{
docHeight:Math.max(document.body.scrollHeight,document.body.clientHeight),
docWidth:Math.max(document.body.scrollWidth,document.body.clientHeight)
}
}else//标准模式
{
return{
docHeight:Math.max(document.documentElement.scrollHeight,document.documentElement.clientHeight),
docWidth:Math.max(document.documentElement.scrollWidth,document.documentElement.clientHeight)
}
}
}
。防止自己的页面被iframe掉的代码
try{
top.location.href;
//跨域的时候我的页面不能访问顶层框架的hostname,否则报错!
if (top.location.hostname != window.location.hostname) {
top.location.href =window.location.href;
}
}
//表示跨域了(跨域时候不能访问hostname/href等所有的信息),
//这时候对top进行URL重定向!
catch(e){
top.location.href = window.location.href;
}
。如何设置弹出框
var result=prompt('What is your name?',"qinliang");
//第一个参数是提示文本,第二个参数是默认值,可以是空字符串
if(result!==null)
{
//如果单机了ok那么返回文本输入域内容,单机了cancel或者没有单机OK而是
//通过其它方法关闭对话框那么就是null!
console.log('welcome'+result);
}
。重视函数节流的概念,让一个函数在一个作用域里面每隔100ms执行一次
function throttle(method,context)
{
clearTimeout(method.tid);//第一次没有tid
method.tid=setTimeout(function(){method.call(context);},100)//每隔100ms执行一次
}
调用方式:
window.onresize=function()
{
throttle(resizeDiv);
//让这个resizeDiv方法在全局作用域里面运行,当页面的大小发生变化的时候,我们每隔100ms调用一次resizeDiv方法,这里没有指定context
//当页面很复杂的时候计算offsetWidth等特别耗时,同时设置height可能需要回流,因此这里用了函数节流
}
。数组去重的方法
Array.prototype.unique=function()
{
var result=[],comb=[];
for(var i=0,j=this.length;i
去重的方法之2如下:(这时候一定要记住:用一个变量把push的长度记录下来而不是用遍历数组长度来完成,这时候时间复杂度是nlogn)
Array.prototype.unique=function() {
var elem,
duplicates = [],
j = 0,
i = 0;
this.sort();//首先排序
while ( (elem = this[i++]) ) {
if ( elem === this[ i ] ) {
j = duplicates.push( i );
//这里的j是必须的
}
}
//duplicates数组里面放的全部是重复的下标[1,3]
//push方法返回修改后数组的长度!
while ( j-- ) {
//移除重复项目!
this.splice( duplicates[ j ], 1 );
}
};
var arr=[2,3,2,3];
arr.unique();
console.log(arr);
。合并数组重复项目(这里是不进行排序的时候需要的代码逻辑不需要排序,但是最多只能出现两个相同的,那是因为删除的位置会发生变化)
Array.prototype.unique = function() {
var a = this.concat();
for(var i=0; i
这种方式是无法删除元素的,请看下面的原因
var arr=[1,2,3,2,2,3];
arr.splice(3,1);
console.log(arr);
//打印[1, 2, 3, 2, 3]
arr.splice(4,1);
console.log(arr);
//打印[1, 2, 3, 2]
//这是为什么无法删除重复元素的根本原因,之所以jQuery可以是因为splice后可以
//保存变化的坐标所以可以删除
。继承的几种方式是什么
//原型链继承
function Super(){
this.name='qinliang';
this.sex='male';
}
Super.prototype.sayName=function(){
console.log(this.name);
}
function Sub(){
Super.call(this);
}
//方法一:原型链继承
/*Sub.prototype=new Super();
var sub=new Sub();
console.log(sub.name);
console.log(sub.constructor);*/
//方法二:原型式继承,原型式继承和原型链一样,当存在数组等引用类型的时候就会存在问题
/*function object(obj){
function F(){}
F.prototype=obj;
return new F();
}
var obj=new Super();
var result=object(obj);
console.log(result.sex);
*/
//方法三:借用构造函数的方式,在Super函数里面添加一句 Super.call(this);
//这种方式会导致父类中定义的函数等对子类是不可见的!
/*var sub=new Sub();
console.log(sub.name);
*/
//方法四:组合式继承的方式,对属性等用借用构造函数的方式来继承而对于方法等用原型的方式来继承
//但是这时候会在原型和对象中都有同名属性,所以原型中的属性会被覆盖掉,所以这是没有必要的!
//因此才有了寄生组合式继承!
/*
//第一步:用原型的方式来继承方法等
Sub.prototype=new Super();
//第二步:在Sub的构造函数里面用Super.call来继承属性
//第三步:对Sub的constructor属性进行重新设置以防丢失,因为这时候的Sub.prototype.constructor的已经是Super了
Sub.prototype.constructor=Sub;
var sub=new Sub();
sub.sayName();*/
//方法五:寄生式构造函数
/*
function object(obj){
function F(){}
F.prototype=obj;
return new F();
}
function parasite(orig){
var obj=object(orig);
obj.sayHi=function(){
console.log('hi');
}
return obj;
}
var sup=new Super();
console.log(parasite(sup).name);*/
//方法六:继承组合式继承原理是我们只要获取父类prototype的一个实例,然后把这个实例作为子类的原型,同时修改其constructor就可以了
/*
function object(obj){
function F(){}
F.prototype=obj;
return new F();
}
function inherit(subType,superType){
var superProtype=object(superType.prototype);//获取父类的原型
superProtype.constructor=subType;//constructor
subType.prototype=superProtype;//修改子类的prorotype!
}
inherit(Sub,Super);
var sub=new Sub();
console.log(sub);*/
。那些方式可以创建对象
//创建对象的几种方式
//方法一:工厂模式
/*
function Factory(name){
var obj=new Object();
obj.name=name;
return obj;
}
console.log(Factory('qinliang'));*/
//方法二:构造函数模式
/*
function constructure(name){
this.name=name;
}
var obj=new constructure('qingliang');
console.log(obj);*/
//方法三:动态原型模式,只会在原型中添加一次,这一点很重要
/*
function dynamic(name,sex){
this.name=name;
this.sex=sex;
if(!this.sayName){//如果存在就不添加了
dynamic.prototype.sayName=function(){
console.log('qinliang');
}
}
}
new dynamic('qinliang','male').sayName();*/
//方法四:原型模式
/*
function proto(){
}
proto.prototype.sayHello=function(){
console.log('say hello');
}
new proto().sayHello();
*/
//方法五:稳妥式构造函数,里面全部没有this等,函数的访问通过闭包原理来完成
/*
function stable(name){
var obj=new Object();
obj.name=name;
obj.sayName=function(){
console.log(name);//通过闭包可以访问外层的对象
}
return obj;
}
stable('qinliang').sayName();*/
//方法六:寄生式构造函数,处理使用new操作符并把这个包装的函数叫做构造函数以外和工厂公式没有任何区别
/*
function parasite(name){
var obj=new Object();
obj.name=name;
obj.sayName=function(){
console.log(this.name);
//这里面的this之所以可以用是因为外面用了new操作符
}
return obj;
}
var obj=new parasite('qinliang');
console.log(obj.name);
*/
。那些1+undefined的输出问题要记牢,其主要原因是undefined在通过Number转换的时候转换成了NaN!
function test(a,b){
return a+b;
}
function test(a,b,c){
return a+b+c;
}
console.log(test(1,2));
//打印NaN
console.log(1+2+undefined);
//打印NaN就可以了!
console.log('1'+undefined);
//打印1undefined是字符串
console.log(undefined+0);
console.log(undefined-1);
console.log(undefined*1);
console.log(undefined/3);
//全部打印NaN,这表明,和undefined一起进行加减乘除运算返回的结果都是NaN
//这一点一定要注意
console.log(undefined-false);
console.log(undefined+true);
console.log(undefined*true);
console.log(undefined/true);
//undefined和布尔值在一起不管是加减乘除都会返回NaN!
console.log(undefined+"q");
console.log(undefined-"q");
console.log(undefined*"q");
console.log(undefined/"q");
//undefine和字符串在一起只有加号能够返回字符串,其它都是NaN!
总之:数字布尔值字符串和undefined的加减乘除运算都会返回NaN,只有一种情况那就是undefind和字符串执行加法运算!
。函数声明提升时候如果变量和函数在一起,那么就会是函数,不管函数和变量谁写在前面都是这样的结果
console.log(typeof a);
var a=1;
function a(){}
//只要变量和函数同时存在的时候,这个函数声明提升的过程以函数为尊贵,也就是不管把函数写在后面还是变量写在后面
//都会打印function
。又论函数声明提升
var foo=1;
function bar(){
if(!foo){
var foo=10;
//打印10,因为这个foo在bar函数的顶部被声明,所以foo这时候为undefined
console.log(foo);
}
}
bar();
。再论函数声明提升,这一次真的是函数声明提升,而不是变量声明提升
var a=1;
function b(){
//这里被提升a成为function了
console.log(typeof a);
a=10;
console.log(a);
return ;
function a(){
//这里还是会被提升
}
}
b();
console.log(a);
。函数调用无法修改函数的作用域,也就是无法修改this指向,这一点很重要
var i='window';
var obj={
i:"test",
m:function(){
console.log(this.i);
//这里的this就是obj,所以打印test
function b(){
console.log(this.i);
}
b();//调用函数b,函数调用无法改变函数作用域,所以这里返回的就是window对象的i对象
}
}
obj.m();
。单例模式的记录
var Singleton=(function(){
console.log('enter');
var privateAttr=0;
function privateMethod(){console.log(privateAttr);};
//私有方法来操作私有属性,这是可以的
return{
publicAttr:true,
publicMethod:function(){privateMethod();privateAttr++;}
//共有方法借助于闭包可以访问私有方法,同时通过该方法也可以修改私有属性
}
})();
Singleton.publicMethod();
//调用共有的方法
console.log(Singleton.publicAttr);
//获取共有的属性
console.log(Singleton===Singleton);
//我们惊奇的发现这里打印了true,表明返回的两个对象是完全相等的
Singleton.publicMethod();
//这时候我们再次调用publicMethod的时候发现上面的“enter”不再打印了,这表示我们只会初始化一次,所以这个对象是同一个对象
//也就是说Signleton只有一个对象存在于内存中
Singleton.publicMethod();
只在明确要求调用的时候才会初始化,否则简单的返回一个对象
//这种方式可以做到只有在使用的时候才会初始化!
var singleton=(function(){
//让他只有调用了才会执行,我们看看下面的代码
var instanced;
//记录是否已经实例化了,返回的对象如果已经实例化了就原样返回,否则才重新创建一个对象
function init(){
return {
publicMethod:function(){
console.log('publicMethod');
},
publicProperty:true
}
}
return{
//只有调用了getInstance方法才回去实例化对象,否则只是简单的返回这一个对象字面量~
getInstance:function(){
if(!instanced){
instanced=init();
}
return instanced;//返回这个对象
}
}
})();
console.log(singleton);
。变态javascript题目
详见变态javascript题目
。if,while等循环条件中定义的函数不会发生声明提升的过程,所以在函数前面,函数中间,函数后面都无法访问,只是简单的作为条件来判断,但是typeof确实会返回undefined。
var x = 1;
//console.log(f);//打印报错
//放在while,for,if等中的函数不会发生函数声明提升,而且在后面也是不能访问到的
if (function f(){}) {
// console.log(typeof f);
//因为在里面和外面都是无法访问到的,都是报错,但是typeof确实返回undefined,这一点很重要
}
//console.log(f);//这时候也是会报错的,这一点很重要!
。如果仅仅是为外面的变量赋值而不是完全重写变量那么这时候是可以的
function c(){
var obj={v:1};
obj.functionx=function(){obj.v=2};
return obj;
}
var c1=c();
c1.functionx();//调用这个函数
console.log(c1.v);//打印2
完全修改了这时候无法反映到外面的
function c(){
var obj={v:1};
obj.functionx=function(){obj={v:4}};
//完全重写了,所以就可以的
return obj;
}
var c1=c();
c1.functionx();//调用这个函数
console.log(c1.v);//打印1
new操作符可以修改的,也就是new会返回到外部
function c(){
this.obj={v:1};
this.func=function(){
this.obj={v:2};
}
}
var obj=new c();
obj.func();
console.log(obj.obj.v);
//打印2,这是因为用new绑定到这个元素上面了
。关于arguments对象的一些注意的地方
function doAdd(num1,num2){
arguments[1]=10;
console.log(arguments[0]+num2);
}
doAdd(5,1);
//(1)arguments的值永远和命名参数的值保持同步,但是不是说这两个字值保存相同的内存空间,他们的内存空间是独立的,但是值是同步的
//(2)但是,如果只是传入了一个参数那么arguments[1]的设置不会反映到命名参数中,这是因为arguments的长度是由传入的参数个数决定的不是由定义参数的个数决定的,这一点很重要。
//(3)没有传入的参数都是undefined。而且在严格模式下对arguments对象做了限制,重写arguments的值会导致语法错误
。关于setTimout的一些注意的地方,setTimout只是把要执行的代码放入定时器,所以他的执行比一般语句都慢
function test(){
//我们必须弄清楚setTimeout代码的执行比一般的都要慢
var a=1;
setTimeout(function(){console.log(a);a=3;},1000);
//这个setTimeout在后面的这个赋值语句之后执行,所以该timeout打印的时候a已经是2了!
a=2;
setTimeout(function(){console.log(a);a=4;},3000);
}
test();
console.log(0);
//打印0,2,3
。多次声明同一个变量会导致js忽略,但是会执行正常的初始化
function test(){
var a=10;
var a=120;
console.log(a);
}
test();
//js从来不会告诉你是否多次声明了同一个变量,遇到这种情况它只会对后续的声明视而不见
//不过他会执行后续声明中的变量初始化
。自执行函数也是函数调用而不是函数引用,无法修改this指向,所以指向window
var name='qinliang';
function outer(){
var self=this;
//这里的self就是外部的obj对象了
var name='fkl';
(function(){
console.log(self);//外部obj
console.log(name);//打印fkl
console.log(this);//自执行函数具有全局作用域,所以是window!也就是我们说的函数调用无法改变作用域
console.log(sex);//报错,没有sex
})();
}
var obj={name:"qinliang",sex:"male"};
var local=outer.bind(obj);
local('female');
自执行函数还是能够访问外部的变量的,这一点很重要的
function test(){
var name="qinliang";
(function(){
console.log(name);//可以访问外部的变量
console.log(this);//还是window
})();
}
var obj={name:"fkl",sex:"female"};
test.bind(obj)();
//显然test里面的this是obj对象,但是有趣的是里面自执行的函数上下文还是window!
。记住不要引用外部的循环变量(用this解决),否则会导致闭包的问题出现
window.onload=function(){
var lists=document.querySelectorAll('li');
for(var i=0,len=lists.length;i
上面用this解决了循环引用的闭包问题,下面就用自执行函数来解决了
window.onload=function(){
var lists=document.querySelectorAll('li');
for(var i=0,len=lists.length;i
。下面还是一个闭包的问题,很简单的问题
function a(){
var i=0;
function b(){
console.log(++i);
}
return b;
}
var c=a();
c();
c();
//打印1,2
。狗每隔两秒叫一次
var pet=function(){
this.message='qinliang';
this.shout=function(){
console.log(this.message);
}
this.waitAndShout=function(){
var that=this;
//每隔两秒调用一次shout方法,这时候特别要注意用setTimeInterval(that.shout,2000)
//是不可以的,因为这时候this没有得到保存
setInterval(function(){that.shout();},2000);
}
}
var pt=new pet();//也是可以new操作的
pt.waitAndShout();
。如何让元素超出用省略号表示
。又论变量类型声明提升的作用:注意这里是直接调用了main方法了,所以this被绑定到window上面了
var foo=1;
function main(){
console.log(foo);
var foo=2;
//变量声明类型提升
console.log(this.foo);
//这里面的this是window对象
this.foo=3;
}
var m1=main();
//打印undefined和1
。arguments[0]()调用就相当于this已经是arguments对象了
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn();
arguments[0]();//arguments={fn:fn,length:2}
//相当于arguments调用方法所以fn中的this就是arguments对象
}
};
obj.method(fn, 1);//打印10,2,因为arguments这时候的长度是2
。函数的声明提升的优先级要高于变量
function fn(a) {
//这里打印函数,因为函数的变量类型声明提升优先级比变量高
console.log(a);
var a = 2;
function a() {}
//而var a=2相当于给a重新赋值了,所以打印2
console.log(a);
}
fn(1);
。还是变量声明类型提升的题目
//这里的a被提升到window作用域最前面了
if('a' in window) {
var a = 10;
//这里也会发生变量类型声明提升
}
console.log(a);
。给基本类型数据添加属性,不报错,但取值时是undefined
var a = 10;
a.pro = 10;
console.log(a.pro + a);
var s = 'hello';
s.pro = 'world';
console.log(s.pro + s);//打印NaN,undefinedhello
。求出出现最多的元素的次数(利用Array对象来完成,如果已经在对象中那么直接加1否则就设置为1)
function getMaxTimes(s){
var arr=s.split('');
var obj={};
for(var i=0;i
。尽然有一种情况下this是指向arguments对象的,这一点很重要,也就是当通过arguments来调用参数的函数的时候,参见这10道javascript笔试题你都会么
function getAge(){
console.log(this);
//这里的this指的就是obj的func函数中的arguments对象
}
var obj={
func:function(fn){
arguments[0]();
//调用函数
}
};
obj.func(getAge,12);
。还是不会
var a = {n: 1}
var b = a;
a.x = a = {n: 2}
console.log(a.x);
console.log(b.x)
//undefined
// Object {n: 2}
连续赋值与求值顺序var a = {n:1};a.x = a = {n:2}; alert(a.x); // --> undefined
。下面的填写代码使得第一个对象的name是name1,第二个对象的name是name2。因为有默认的undefined
function obj(name){
//第一部分:这下面是填入的代码
if(name){
this.name=name;
return this;
}
}
//第二部分:这里是填入的代码
obj.prototype.name = "name2";
var a = obj("name1");
var b = new obj;
//这里和new obj()是一样的,但是我们这里通过obj构造出来的对象的name却成为了undefined,这是我们不需要的结果!
console.log(a.name);
console.log(b.name);
。一个很好玩的方法的输出,注意:对于apply.call和call.apply都是需要数组类型的参数,但是apply.call必须设置this单独传入
var a = Function.prototype.apply.call(function(a){return a;}, {name:"qinliang"},[0,4,3]);
console.log(a);
//这时候打印undefined,如果是 Function.prototype.apply.call(function(a){return a;}, {name:"qinliang"},[0,4,3]);就打印0
第二个有趣的方法:
var a = Function.prototype.call.apply(function(a){
console.log(this);
//这时候this为0
return a;
}, [0,4,3]);
console.log(a);
//打印4,因为第一个变成了this了!因为call必须单个单个传递参数,这个必须要理解
。下面几个函数的用法
var arr=[1,4,3,2];
/*----------------下面是测试splice方法--------*/
// var arr1=arr.splice(0,2);
//console.log(arr1);
//打印[1,4]
// console.log(arr);
//打印[3,2]
/*---------这里是测试reduce方法的用法--------*/
var sum=arr.reduce(function(pre,cur,index,array){
return pre+cur;
});
console.log(sum);
var sum1=arr.reduce(function(pre,cur,index,array){
return pre+cur;
},10);
console.log(sum1);
//这里的第二个参数是累计的时候的原始值,第一个打印10,第二个打印20
var sum2=arr.reduceRight(function(pre,cur,index,arr){
return pre+cur;
});
console.log(sum2);
var sum3=arr.reduceRight(function(pre,cur,index,a){
return pre+cur;
},10);
console.log(sum3);
//reduce和reduceRight是一样的,只是相加的方向不一样
/*-------------下面模拟队列的方法(push+shift)----------------*/
var quque=[3,5,2,1];
quque.push(100,1000);
//可以一次性插入两项
var delt=quque.shift();
console.log(delt);
//获取第一项,下面使用unshift+pop方法从相反的方向来模拟对象
var quque1=[5,4,3,2,1];
quque1.unshift(100,1000);
//这时候queque1为[100, 1000, 5, 4, 3, 2, 1]
var q=quque1.pop();
console.log(q);
//打印1,也就是这里使用unshift+pop实现了反向对象
/*------------------下面模拟栈方法(push+pop)----------*/
var stack=[1,2,3,4,5];
stack.push(100,1000);
var stackValue=stack.pop();
console.log(stackValue);
//打印1000
for(var i=1;i<=3;i++){
setTimeout(function(){
console.log(i);
},0);
};
。定义匿名自执行函数
(function test(arg){
console.log('test'+arg);
})(123);
//有函数名可以自执行,但是前后都应该有括号
(function(arg){
console.log('test'+arg);
})(123);
//没有函数名也是可以自执行,但是前后也应该有括号
function test(){
}();//这种方式定义的函数会报错