JavaScript call()和Prototype - Slice Function

时间:2016-05-21 22:43:58

标签: javascript call slice apply

我正在JavaScript中阅读slice上的MDN Article。我理解除了第二个例子之外的所有内容,标题为 Array-Like Objects

它说我们可以通过将slice作为我们自己的函数来简化第一个例子:

var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);

function list() {
  return slice(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

我不明白call在第二行prototype之后可能会如何发现。

我通常会以Array.prototype.slice.call(arguments)或类似的形式看到它。

我不了解前两行的流程以及它们如何生成此slice函数。

5 个答案:

答案 0 :(得分:4)

Function.prototype.call()的{​​{3}}帮助我解决了这个问题。

我能回答的最简单的方式:

  

在javascript中,Function有一个名为call的方法。功能是一个   对象,所有对象都从它们继承方法和属性   原型。

因此,Array.prototype.slice.call(arguments)的示例显示您在切片函数上调用了调用方法。

您感到困惑的代码中的第二行:var slice = Function.prototype.call.bind(unboundSlice);显示属于Function原型的调用方法。

如果您仍然感到困惑,请结帐MDN article

1函数是对象。

2"每个JavaScript对象都有一个原型。"

3"原型也是一个对象。"

4"所有JavaScript对象都从其原型继承其属性和方法。"

换句话说,回到最简单的方式回答这个问题:在javascript中,一个Function有一个名为call的方法。

至于理解bind的作用,JavaScript Prototypes文章中的that = this vs .bind示例有助于了解正在发生的事情。

如果这令人困惑,请确保您理解this

答案 1 :(得分:3)

tl;博士:

var slice = Function.prototype.call.bind(unboundSlice);

是一种简短的写作方式:

var slice = function(value, start, end) {
  return unboundSlice.call(value, start, end);
};

让我们考虑第二行:

Array.prototype.slice.call(arguments)

.slice是一种提取数组子集的数组方法。它的值为this.call是每个函数都有的方法,它允许您为函数执行设置this值。因此,上述行允许我们执行slice作为arguments的方法,而不必改变arguments本身。我们可以完成

arguments.slice = Array.prototype.slice;
arguments.slice();

但这不是那么干净。

现在看

Function.prototype.call.bind(unboundSlice);

如上所述,.call每个函数所具有的方法。它也在this上运行,预计它将成为一种功能。它调用this并将该函数的this值设置为第一个参数。你可以认为call

类似
function call(thisValue, arg1, arg2, ...) {
   return this.apply(thisValue, [arg1, arg2, ...]);
}

请注意它如何将this称为函数。

.bind也是每个函数都有的方法。它返回一个新函数,其this值固定为您传入的第一个参数。

让我们考虑call.bind(unboundSlice)的结果函数是什么样的:

function boundCall(thisValue, arg1, arg2, ...) {
   return unboundSlice.apply(thisValue, [arg1, arg2, ...]);
}

我们只是将this替换为unboundSliceboundCall现在将始终致电unboundSlice

答案 2 :(得分:2)

sliceArray.prototype的属性,它期望其this对象与数组类似。您可以在类似于Array的对象(具有length属性并具有可以索引的属性)上使用它,它们没有自己的切片函数,如下所示:

Array.prototype.slice.call(arraylikething);

这是很多打字,所以我们可以做一个功能来做同样的事情:

var slice = function(arraylikething){
    return Array.prototype.slice.call(arraylikething);
};

JavaScript提供Function.prototype.bind将函数绑定到指定的this对象。所以我们可以更轻松地完成同样的事情:

var slice = Function.prototype.call.bind(Array.prototype.slice);

bind创建一个新函数,返回call的结果,其this对象设置为Array.prototype.slice,与我们上面手动执行的操作相同,相当于你的代码。

答案 3 :(得分:1)

Chris Dillinger的回答是正确的,内容丰富。但这是考虑它的另一种方式。实质上,您被要求定义

Function.prototype.call.bind(Array.prototype.slice)

你可以这样看:

fn.bind(context) 
    ==>  function(...args) {return context.fn(...args);} 
            // 1. definition of `bind` (oversimplified, but enough for this case)

fn.bind(unboundSlice) 
    ==>  function(...args) {return unboundSlice.fn(...args);}  
            // 2. substitute `unboundSlice` for `context`

Function.prototype.call.bind(unboundSlice)
    ==>  function(...args) {return unboundSlice[Function.prototype.call](...args);} 
            // 3. substitute `Function.prototype.call` for `fn`.

Function.prototype.call.bind(unboundSlice)
    ==>  function(...args) {return unboundSlice[.call(...args);} 
            // 4. walk the prototype chain

Function.prototype.call.bind(Array.prototype.slice)
    ==>  function(...args) {return Array.prototype.slice.call(...args);}
            // 5. substitue `Array.prototype.slice` for `unboundSlice`

唯一有点棘手的步骤是第4步,你必须意识到所有函数都从其原型链继承call方法,因此在它们上调用call仅仅是另一种调用函数本身的方法。

答案 4 :(得分:0)

在第一行中,Array.prototype.slice(这是一种方法)只是通过unboundSlice引用。您实际上是从Array.prototype“提取”切片方法。

在第二行中,同样的事情发生在Function.prototype.call上,这也是ALL函数的一种方法。 (它在Function.prototype中定义,并由所有函数继承)。

接下来,通过使用.bind(unboundSlice),调用函数的this值绑定到Array.prototype.slice的引用,这实际上与Array.prototype.slice.call()的结果相同,其中调用也是将this绑定到slice,因为它是它的一种方法,因为它被调用了。

最后,绑定调用方法通过var slice;

引用

这里的一般想法是,您可以在另一个上下文(全局范围)中使用数组方法(切片)的功能。

现在,不是在call已经是slice的方法时调用slice,而是将this绑定为call // Dataset = list of players and their attributes private final ArrayList<PlayerData> dataSet; private final List<PlayerData> filteredList; private ArrayList<PlayerData> originalItems; public static class MyViewHolder extends RecyclerView.ViewHolder { TextView textViewName; TextView textViewPos; ImageView imageViewFace; ImageView imageTeamLogo; TextView textViewTeam; public MyViewHolder(View itemView) { super(itemView); this.textViewName = (TextView) itemView.findViewById(R.id.textViewName); this.textViewPos = (TextView) itemView.findViewById(R.id.textViewPos); this.imageViewFace = (ImageView) itemView.findViewById(R.id.imageViewFace); this.imageTeamLogo = (ImageView) itemView.findViewById(R.id.ImgTeamLogo); this.textViewTeam = (TextView) itemView.findViewById(R.id.textViewTeam); } } //dataset = data from Main Activity public CustomAdapter(ArrayList<PlayerData> data) { this.dataSet = data; this.filteredList = data; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.cards_layout, parent, false); MyViewHolder myViewHolder = new MyViewHolder(view); return myViewHolder; } @Override public void onBindViewHolder(final MyViewHolder holder, final int Position) { // called everytime user scrolls up or down TextView textViewName = holder.textViewName; TextView textViewPos = holder.textViewPos; ImageView imageViewFace = holder.imageViewFace; ImageView imageViewLogo = holder.imageTeamLogo; TextView textViewTeam = holder.textViewTeam; textViewName.setText(dataSet.get(Position).getName()); textViewPos.setText(dataSet.get(Position).getPos()); textViewTeam.setText(dataSet.get(Position).getTeam()); Context context = imageViewFace.getContext(); Context context2 = imageViewLogo.getContext(); Picasso.with(context) .load(dataSet.get(Position).getFace()) .placeholder(R.drawable.ic_launcher) .error(R.drawable.ic_3d_rotation) .into(holder.imageViewFace); Glide.with(context2).load(dataSet.get(Position).getTeamLogo()).into(holder.imageTeamLogo); } @Override public int getItemCount() { return dataSet.size(); } public void animateTo(ArrayList<PlayerData> players) { //checks what query found and adds / removes results // problem is, it is not adding back results applyAndAnimateRemovals(players); applyAndAnimateAdditions(players); applyAndAnimateMovedItems(players); } private void applyAndAnimateRemovals(ArrayList<PlayerData> newPlayers) { for (int i = dataSet.size() - 1; i >= 0; i--) { final PlayerData player = dataSet.get(i); if (!newPlayers.contains(player)) { removeItem(i); } } } private void applyAndAnimateAdditions(ArrayList<PlayerData> newPlayers) { for (int i = 0, count = newPlayers.size(); i < count; i++) { final PlayerData player = newPlayers.get(i); if (!dataSet.contains(player)) { addItem(i, player); } } } private void applyAndAnimateMovedItems(ArrayList<PlayerData> newPlayers) { for (int toPosition = newPlayers.size() - 1; toPosition >= 0; toPosition--) { final PlayerData player = newPlayers.get(toPosition); final int fromPosition = dataSet.indexOf(player); if (fromPosition >= 0 && fromPosition != toPosition) { moveItem(fromPosition, toPosition); } } } public PlayerData removeItem(int position) { final PlayerData player = dataSet.remove(position); notifyItemRemoved(position); return player; } public void addItem(int position, PlayerData player) { dataSet.add(position, player); // this code is not working for whatever reason) notifyItemInserted(position); } public void moveItem(int fromPosition, int toPosition) { final PlayerData player = dataSet.remove(fromPosition); dataSet.add(toPosition, player); notifyItemMoved(fromPosition, toPosition); } } 的值public class MainFragment extends Fragment implements SearchView.OnQueryTextListener { public static MainFragment newInstance() { return new MainFragment(); } private static RecyclerView recyclerView; private static CustomAdapter adapter; private static ArrayList<PlayerData> data; private RecyclerView.LayoutManager layoutManager; static View.OnClickListener myOnClickListener; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_main, container, false); recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView); return view; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); setHasOptionsMenu(true); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); recyclerView.setItemAnimator(new DefaultItemAnimator()); data = new ArrayList<PlayerData>(); addPlayers(); adapter = new CustomAdapter(data); recyclerView.setAdapter(adapter); } public interface Constants { String LOG = "com.vogella.testapp"; } private void addPlayers() { PlayerData player = new PlayerData("Chris Smith", "#", R.drawable.auser, R.drawable.aquestion, "First", 0); data.add(0,player); player = new PlayerData("Bobby Richars","#", R.drawable.auser, R.drawable.aquestion, "Second", 9); data.add(player); player = new PlayerData("Steven Williams", "#", R.drawable.auser, R.drawable.aquestion, "Third", 1); data.add(player); player = new PlayerData("Tony Lloyd", "#", R.drawable.auser, R.drawable.aquestion, "Fourth", 2); data.add(player); player = new PlayerData("Henry Gates", "#", R.drawable.auser,R.drawable.aquestion, "Fifth", 3); data.add(player); player = new PlayerData("Robert Marks", "#", R.drawable.auser, R.drawable.aquestion, "Sixth", 4); data.add(player); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.menu_main, menu); final MenuItem item = menu.findItem(R.id.action_search); final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item); searchView.setOnQueryTextListener(this); } @Override public boolean onQueryTextChange(String query) { final ArrayList<PlayerData> filteredModelList = filter(data, query); adapter.animateTo(filteredModelList); recyclerView.scrollToPosition(0); return false; } @Override public boolean onQueryTextSubmit(String query) { return false; } private ArrayList<PlayerData> filter(ArrayList<PlayerData> players, String query) { query = query.toLowerCase(); final ArrayList<PlayerData> filteredModelList = new ArrayList<>(); for (PlayerData player : players) { final String text = player.getName().toLowerCase() + player.getPos().toLowerCase()+ player.getTeam().toLowerCase(); if (text.contains(query)) { filteredModelList.add(player); } } return filteredModelList; } @Override public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); return true; } } 为了达到同样的行为。