本文共 14257 字,大约阅读时间需要 47 分钟。
function Foo() { getName = function () { alert (1); }; return this;}Foo.getName = function () { alert (2);};Foo.prototype.getName = function () { alert (3);};var getName = function () { alert (4);};function getName() { alert (5);} //请写出以下输出结果:Foo.getName();getName();Foo().getName();getName();new Foo.getName();new Foo().getName();new new Foo().getName();
这道题的主要综合考察了的JavaScript的综合能力,包含了变量定义提升、this指针指向、运算符优先级、原型、继承、全局变量污染、对象属性及原型属性优先级等知识.
Foo.getName();
先简要的看一下上半部分的代码: 首先创建了一个名为Foo的函数;
接下来给Foo的静态属性getName存储了一个匿名函数; 给Foo的原型对象创建一个名为getName的匿名函数; 之后又通过函数变量表达式创建了一个getName的函数; 最后再声明一个叫getName函数。那么,第一问就毫不犹豫的输出2;
getName();
第二问,直接调用getName函数。则调用当前作用域内的getName函数,所以这里应该直接把关注点放在4和5上.
4与5的区别在函数声明和函数表达式两种方式的区别:var getName; console.log(getName)//function getName() {alert(5);} getName()//5 function getName() { alert (5); }
var getName; console.log(getName)//undefined getName()//Uncaught TypeError: getName is not a function var getName = function() { alert (4); }
那么, 答案也就很明确了, 为4.
Foo().getName();
首先,执行了Foo函数,并返回了this,this的指向是由所在函数的调用方式决定的。而此处的直接调用方式,this指向window对象.遂Foo函数返回的是window对象,相当于执行window.getName();即执行getName()由于在函数Foo执行的过程中,其函数内部的getName的赋值操作会将var getName = function () { alert (4);}给赋值为function () { alert (1);}, 此时全局 作用域中的getName就变为function () { alert (1);}. 从而该答案为1.
getName();
该问题和三个相同,由于getName被修改过了.所以答案为1.
new Foo.getName();
该问题要考虑函数执行的优先级顺序:
new (Foo.getName)();
.成员访问(18)->new有参数列表(18)
new Foo().getName();
该执行顺序可以看做是:
(new Foo()).getName();
new有参数列表(18)->.成员访问(18)->()函数调用(17);
new new Foo().getName();
同样是运算符优先级问题.
new ((new Foo()).getName)();
new有参数列表(18)->new有参数列表(18);
先初始化Foo的实例化对象,然后将其原型上的getName函数作为构造函数再次new,所以最终结果为3;
function Foo() { getName = function () { alert (1); }; return this;}Foo.getName = function () { alert (2);};Foo.prototype.getName = function () { alert (3);};var getName = function () { alert (4);};function getName() { alert (5);}//答案:Foo.getName();//2getName();//4Foo().getName();//1getName();//1new Foo.getName();//2new Foo().getName();//3new new Foo().getName();//3
解析: typeof: 只能区分原始类型的函数
并不能进一步细致区分出对象的类型名;方法如下:
var bool=父对象.isPrototypeOf(子对象);
obj.constructor===构造函数; 问题: constructor 是隐藏属性,并不推荐使用;
obj instanceof 构造函数; 强调: 以上两种方式都不仅检查直接的父类型,也检查是不是整个原型链的
原理: 在每个对象内都有一个隐藏属性 Class
属性: class保存了对象创建时的最初类型;不会随着继承关系的改变而改变; 思路: 只有Object.prototype 中最原始 toString() 才能输出 class 属性 返回值: [object class属性值]; //Object Array Date易错点: 使用子对象.toString(), 容易被父对象中的toString()所重写
解决: 用call 强行调用Object.prototype中的 toString(); Object.prototype.toString.call(obj);
作用: 专门判断任何一个对象是不是Array类型 语法: var bool=Array.isArray(obj); ** 第四种 第五种方法都是严格验证 **
答: 如果只允许指定类型的子对象才能使用的函数,必须放在原型对象中,继承使用 如果希望不限制类型,所有对象都使用的函数,可以直接放在构造函数上: 比如 : sort() push() isArray() Array.prototype.sort(); Array.prototype.push(); Array.isArray();
this到底是一种什么机制?
this是在运行时进行绑定的,并不是在编写的时候进行绑定的,它的上下文取决于函数调用时的各种条件.this的绑定和函数声明的位置没有任何关系,只取决于函数调用时的方式也被称为调用位置. 当函数在调用时, 会创建一个活动记录(AO, 也被称作为上下文). 这个记录会包含函数在哪里被调用(调用栈), 函数的调用方式, 传入的参数等信息. this就是记录这一属性的, 会在函数执行的过程中用到.调用位置就是函数在代码中被调用的位置(而不是声明的位置). 来个小例子, 分析下调用栈和调用位置:
function baz(){ //当前的调用栈是: baz //因此, 当前调用位置是全局作用域 console.log("baz"); bar(); // bar的调用位置 }function bar(){ //当前的调用栈是: baz -> bar //因此, 当前的调用位置在baz中 console.log("bar"); foo(); // foo的调用位置}function foo(){ //当前调用栈是: baz -> bar -> foo //因此,当前的调用位置在foo中 console.log("foo");}baz(); // baz的调用位置
真正地分析出函数的调用位置, 就可以理解this的绑定了.
首先需要找到调用位置, 然后判断需要应用下面四条规则中的那一条.
这是最常用的函数调用类型: 独立函数使用.
function foo(){ console.log(this.a);}var a = 2;foo();
当调用foo()时, this.a 被解析为全局变量a. 因为函数调用时应用了this的默认绑定,因此this会指向全局.
在代码中, foo()直接使用不带任何修饰的函数引用进行调用, 只能认为是默认绑定.
该规则是调用位置是否具有上下文对象, 或者说是否被某个对象拥有或者包含.
function foo(){ console.log(this.a);}var obj2 = { a : 42; foo : foo; //foo的调用位置};var obj1 = { a : 2; obj2 : obj2;};obj1.obj2.foo(); //42
首先, foo()是在全局中被声明, 然后被当做引用属性添加到obj2中. 隐式绑定规则会把函数调用中的this绑定到这个上下文对象中. 因此调用foo时的this被绑定在obj2中.
对象属性引用链中只有上一层或者说最后一层在调用位置中作用.
当隐式绑定的函数丢失绑定对象, 就会默认的转为默认绑定. 从而将this绑定到全局对象或者undefined上, 取决于是否为严格模式.
function foo(){ console.log(this.a);}var obj = { a : 2; foo : foo;}var bar = obj.foo;var a = "oops, global"; //a为全局对象 bar(); // "oops, global"
在这个例子中:
var bar = obj.foo;
bar只是引用了foo函数的本身, 和obj并没有任何关系. 则bar()就是对foo的直接调用. 因此为默认绑定.
再来一个更有意思的例子:function foo(){ console.log(this.a);}function doFoo(fn){ //fn 其实引用的是foo fu(); //调用位置}var obj = { a : 2; foo : foo;};var a = "oops, global"; //a为全局对象 doFoo(obj.foo); // "oops, global"
首先, 在全局作用域中声明了foo的函数; 然后被添加进obj的对象中;然后doFoo函数将foo这个函数作为参数传入进去, 并在其内部进行调用.
在这个例子中, 参数传递被作为一种隐式的赋值. 另外, 在回调函数中, 丢失this的绑定也是比较常见的现象.当我们不想在对象的内部包含函数引用,而想在某个对象上强制调用函数?
为此,JavaScript提供了call(...), apply(...)和bind(...)方法. call()/apply()/bind():此外, 在JavaScript语言和宿主环境中许多的新的内置函数, 其作用和bind(...)一样, 去报你的回调函数使用指定的this. 比如 [1,2,3].forEach(for, obj ); 等等...
new可以影响函数调用时this绑定行为的方法, 我们称之为new绑定.
"构造函数"是类中的一些特殊方法, 使用new初始化时会调用类中的构造函数. 通常的形式是这样的:
something = new MyClass(...);
构造函数:
在JavaScript中, 构造函数只是一些使用new操作符时被调用的函数. 它们并不会属于某个类, 也不会实例化一个类. 实际上, 它们甚至都不能说是一种特殊的函数类型, 它们只是被new操作符调用的普通函数而已.
在使用new来调用函数, 或者说是发生构造函数的调用时, 会自动执行下面的操作.
当判断先根据绑定的优先级来判断函数的调用位置是相应的哪条规则. 可以按照下面的顺序进行判断:
var bar = new foo();
var bar = foo.call(obj2);
var bar = obj1.foo();
var bar = foo();
对象是某个特定引用对象的实例.新对象使用new操作符后面跟一个构造函数来创建的.构造函数本身就是一个普通函数,只不过该函数是由创建新对象的目的而被定义.
在创建Object实例的方式有两种:
var person = new Object();person.name = "nico";persom.age=12;
var person = { name : "nico"; age : 12;}
下面来一个小小例子:
function Person(name,age){ this.name=name; this.age=age; this.sayName=function(){ alert(this.name); };} var person=new Person("张三",20); //此处为 构造对象,构造对象的话,返回的新对象是由解析器自己生成的。 var person=Person("张三",20); //报错 person undefined 此处为普通函数调用,又没有给定返回值,出错。 person.sayName();
注意:构造函数在没有返回值的情况下,默认返回新对象实例。
则可以得出,
对于return回的内容:
如果函数返回值为常规意义上的值类型(Number、String、Boolean)时,new 函数将会返回一个该函数的实例对象,而如果函数返回一个引用类型(Object、Array、Function),虽然new函数与直接调用函数产生的结果等同,但是是两个不同的过程,一个是构造对象、一个是函数调用。
构造函数:
在JavaScript中, 构造函数只是一些使用new操作符时被调用的函数. 它们并不会属于某个类, 也不会实例化一个类. 实际上, 它们甚至都不能说是一种特殊的函数类型, 它们只是被new操作符调用的普通函数而已.
在使用new来调用函数, 或者说是发生构造函数的调用时, 会自动执行下面的操作.
var arr= ["a","b","c"];arr.forEach(function(el,index){ console.log(el,index)})
结果:
a 0b 1c 2
arr.map(function(el,index){ return el+="m"})
则新数组为: ["am", "bm", "cm"]; 原数组不变: ["a", "b", "c"];
var arr = [12, 5, 8, 130, 44];arr.filter(function(value){ return value>10});
也等价于:
arr.filter(item=>item>10)
var arr = [12, 5, 8, 130, 44];arr.some(function(value){ // true return value>10});
var arr = [12, 5, 8, 130, 44];arr.every(function(value){ // false return value>10});
var ages = [3,12,4165,131];ages.find(age=>age>=18) //4165
语法:
var result = arr.reduce(function(prev,val,i,arr){ //prev return prve+val;},default)
其中,匿名函数最多可放四个参数,后面两个参数可以省略;default可作为默认值传入参数运算,也可省略;
var arr = [1,2,3,4,5];arr.reduce(function(a,b){return a+b;},10); //输出25;
节点迭代器:Nodelterator
鄙视题:定义函数,遍历指定父节点下所有子元素 HTML文件如下:
遍历节点树 Hello World !
方法如下:
//Step1: 定义函数,仅遍历指定父元素下的直接子元素function getChildren1(parent){ console.log(parent.nodeName); var children=parent.children; for(var i=0,len=children.length;i
方法二:循环方式:
创建元素节点迭代器 使用doucment.createNodeIterator()方法,创建它的新实例.这个方法接受四个参数;
whatToShow常用取值,如下所示:
function getChildren2(parent){ //创建节点迭代器对象: var iterator=document.createNodeIterator( parent, - NodeFilter.SHOW_ELEMENT, null, false //parent:表示作为搜素起点的树中的节点; // - NodeFilter.SHOW_ELEMENT:表示显示元素节点 //null:该位置表示一个NodeFilter对象, //或者一个表示应该接受还是拒绝某种特定节点的函数,一般写为null; //false 该位置表示是否扩展实体引用; ); //反复调用iterator的nextNode方法跳到下一个 do{ var node=iterator.nextNode(); if(node!=null) console.log(node.nodeName); else break; }while(true);}getChildren2(document.body);//测速前,暂时删除方法中的console.logconsole.time("getChildren1");//开始getChildren1(document.body);console.timeEnd("getChildren1");//停止console.time("getChildren2");getChildren2(document.body);console.timeEnd("getChildren2");
已知如下数组:
var arr = [[1,2,2],[3, 4, 5, 5],[6, 7, 8, 9,[11,12,[12,13,[14]]]],10];
编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组 思路:先转换成字符串,然后去掉重复部分,排序; arr.toString().split(","); //强制转换为字符串,并拼成数组 //去重
var str=[];for(var i=0;i
完整代码:
var arr = [[1,2,2],[3, 4, 5, 5],[6, 7, 8, 9,[11,12,[12,13,[14]]]],10];arr=arr.toString().split(",");var str=[];for(var i=0;i
在JS中 null>0为false,nul==0为false,为什么null>=0为true?
解析: 至于深层次理论我探讨不了; 但在<<JavaScript高级程序设计>>中这样写道:
判断一个数是不是质数? 原理: 利用对象的快速查找,避免利用循环对数组的遍历,从而提高程序的执行效率.
var isPrime=(function(){ var hash={}; return function(n){ if(!isNaN(n)&&n>1){ //排除不是正整数的情况 if(n<=3) return true; // 排除2和3的情况 else if(n%2==0) return false; //排除偶数的情况 else if(hash[n]!==undefined){ console.log("不用执行for循环..."); //若hash对象中已经有了则直接返回,避免再次判断 return hash[n]; }else{ console.log("执行for循环..."); for(var i=3;i<=Math.sqrt(n);i+=2){ if(n%i==0){ hash[n]=false; return false; } } hash[n]=true; return true; } }else throw new Error( "必须输入>1的数字"); }})();
冒泡排序
插入排序
原理: i从1开始向右遍历每一个数 将i位置的元素临时保存在变量t中
var arr=[4,2,5,3,1];function insertSort(arr){ for(var i=1;i=0&&arr[p]>=t){ arr[p+1]=arr[p]; p--; } arr[p+1]=t; }}insertSort(arr);console.log(arr);//1,2,3,4,5;
var arr=[6,3,1,5,4,7,2];function quickSort(arr){ if(arr.length<=1) return arr; else{ var c=parseInt((arr.length-1)/2); var cv=arr.splice(c,1)[0]; var left=[],right=[]; for(var i=0;i
最近在http://www.codewars.com上练习js的题, 受益颇多;
class SmallestIntegerFinder { findSmallestInt(args) { return Math.min(...args) }}
class SmallestIntegerFinder { findSmallestInt(args) { return args.sort((a,b)=>a-b)[0]; }}
alphabet_position("The sunset sets at twelve o' clock.");return "20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11";
我的答案:
function alphabetPosition(text) { var result = []; for(var i=0; i=97? test-96:test-64; if(we>=1 && we<=26){ result.push(we); } } return result.join(" ");}var a = alphabetPosition("The sunset sets at twelve o' clock.");console.log(a)
缺点: 使用了数组的api,有了一些不必要的操作;
best:function alphabetPosition(text) { var result = ""; for (var i = 0; i < text.length; i++){ var code = text.toUpperCase().charCodeAt(i) if (code > 64 && code < 91) result += (code - 64) + " "; } return result.slice(0, result.length-1);}
e.g.:
songDecoder("WUBWEWUBAREWUBWUBTHEWUBCHAMPIONSWUBMYWUBFRIENDWUB")
// => WE ARE THE CHAMPIONS MY FRIEND
我的答案:
function songDecoder(song){ return song.replace(/(WUB)+/g," ").trim();}
best:
function songDecoder(song){ return song.split('WUB').filter(Boolean).join(' ');}
自己的方法没做出来, 多多少少有点bug;
大神们的办法:
String.prototype.toJadenCase = function () { return this.replace(/(^|\s)[a-z]/g, function(x){ return x.toUpperCase(); });};
String.prototype.toJadenCase = function () { return this.split(" ").map(function(word){ return word.charAt(0).toUpperCase() + word.slice(1); }).join(" ");}
css方式:
text-transform 值:
accum("abcd"); // "A-Bb-Ccc-Dddd"accum("RqaEzty"); // "R-Qq-Aaa-Eeee-Zzzzz-Tttttt-Yyyyyyy"accum("cwAt"); // "C-Ww-Aaa-Tttt"
我的答案:
function accum(s) { var str = ""; for (var i = 0; i < s.length; i++) { var str1 = s[i]; for (var j = 0; j <=i; j++) { if (j==0) { str+=str1.toUpperCase(); }else{ str+=str1.toLowerCase(); } } str+="-"; } return str.slice(0,-1);}
大佬就是大佬:
function accum(s) { return s.split('').map((c, i) => (c.toUpperCase() + c.toLowerCase().repeat(i))).join('-');}
大佬的方法:
function friend(friends){ return friends.filter(n => n.length === 4)}
我的方法:
function friend(friends){ var arr = []; for(var a = 0; a
原文
转载地址:http://ypwii.baihongyu.com/